c++ - C++中的显式关键字是什么意思?

  显示原文与译文双语对照的内容

有人在评论中发表了关于 explicit 关键字在 C++ 中的含义的评论。 那么,这意味着什么?

时间:

在 C++ 中,允许编译器进行一个隐式转换以将参数解析为函数。 这意味着编译器可以使用参数构造函数来从一种类型转换为另一种类型,以便获得参数的正确类型。 下面是一个带有构造函数的示例类,可以用于隐式转换:


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)) 显式调用转换

你可能想要这样做的原因是避免意外的构造,从而隐藏 Bug 。 虚构的例子:

  • 你有一个 MyString(int size) 类,它包含构造给定大小的字符串的构造函数。 你有一个函数 print(const MyString&),你用 print(3) 调用它。 你希望它打印" 3",但它打印的字符串长度为 3 。

假设你有一个类字符串


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) 构造函数。 但这不是用户可能想要的。 为了避免这种情况,我们可以将类的构造函数定义为显式。


class String { 
public: 
 explicit String (int n);//allocate n bytes
 String(const char *p);//initialize sobject with string p 
} 

在 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);

成为compile-time错误。如果要传递临时 Buffer 对象,必须显式执行以下操作:


useBuffer(Buffer(4));

总之,如果你的single-parameter构造函数将参数转换为类的对象,你可能不想使用 explicit 关键字。 但是如果你有一个简单的构造函数,只需要一个参数,那么你就应该将它声明为 explicit,以防止编译器意外地使用意外的转换。

explicit 关键字使转换构造函数成为non-conversion构造函数。 因此,代码容易出错。

这个答案是关于使用/不带显式构造函数的对象创建,因为它在其他的答案中没有被覆盖。

请考虑以下没有显式构造函数的类:


class Foo
{
public:
 Foo(int x) : m_x(x)
 {
 }

private:
 int m_x;
};

类Foo的对象可以 2种方式创建:


Foo bar1(10);

Foo bar2 = 20;

根据实现的不同,实例化类Foo的第二种方式可能很混乱,或者不是程序员想要的。 将 explicit 关键字添加到构造函数将生成编译器错误 Foo bar2 = 20;

通常它是 好习惯single-argument构造函数声明为 explicit,除非你的实现特别禁止它。

还要注意的是,具有

  • 所有参数的默认参数,或者
  • 第二个参数的默认参数

既可以用作single-argument构造函数。 所以你可能想让这些也是 explicit

一个例子当你故意地 想使你的single-argument构造函数显式的是如果要创建函子( 看看这个'add_x'在中声明的结构这里属性的答案) 。 在这种情况下,创建一个对象为 add_x add30 = 30; 可能会有意义。

这里的是显式构造函数上的一个好 write-up 。

这已经被讨论过( 什么是显式构造函数 ) 。 但我必须说,它缺少这里的详细描述。

此外,让你的一个参数构造函数( 包括用于 arg2,arg3,。的默认值,。) 成为已经声明的总是一个好的编码实践。 C++ 一样:如果你不- 你会希望你做。。

类的另一个好实践是使复制构造和赋值私有( a 。k 。 禁用它),除非你真的需要实现它。 这就避免了在使用 C++ 为你创建默认的方法时最终的指针副本。 另一种方法是从 boost::noncopyable 派生。

explicit -keyword可以用于强制将构造函数显式地称为 。 。


class C{
public:
 explicit C(void) = default;
};

int main(void){
 C c();
 return 0;
}

构造函数前面的explicit -keyword告诉编译器只允许显式调用这里构造函数。

explicit -keyword调用也用于user-defined类型转换运算符:


class C{
public:
 explicit inline operator bool(void) const{
 return true;
 }
};

int main(void){
 C c;
 bool b = static_cast<bool>(c);
 return 0;
}

在这里,explicit -keyword只强制显式强制转换有效,所以 bool b = c; 在这里情况下将是无效的转换。 在这样的情况下,explicit -keyword可以帮助程序员避免隐式的无意识的转换。

下面是 C++ http://www.glenmccl.com/tip_023.htm 中显式的一个例子

构造函数附加隐式转换。为了抑制隐式转换,需要声明带有参数的构造函数。

在C++11中,你还可以指定一个"运算符类型( )"具有这样的关键字 http://en.cppreference.com/w/cpp/language/explicit,你可以使用该规范在显式转换中使用运算符,并直接初始化对象。

P.S 。当使用由用户( 通过构造函数和类型转换运算符) 定义的转换时,只允许使用一个隐式转换级别。 但是你可以将这里转换与其他语言转换组合起来

  • 向上积分等级( char到 int,float到 double ) ;
  • standart转换( int到 double ) ;
  • 将对象指针转换为基类和 void* ;
...