显式关键字是什么意思?

时间:2020-03-06 14:36:01  来源:igfitidea点击:

" explicit"关键字在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))显式调用转换。

我们可能要执行此操作的原因是为了避免可能隐藏错误的意外构造。人为的例子:

  • 我们有一个MyString(int size)类,该类的构造函数构造了给定大小的字符串。我们有一个函数print(const MyString&),并且我们调用了print(3)(当我们实际上打算调用print(" 3")`时)。我们希望它打印" 3",但是它将打印一个长度为3的空字符串。

假设我们有一个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
};

在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",以防止编译器因意外转换而使我们感到惊讶。

已经讨论过了(什么是显式构造函数)。但我必须说,它缺少此处提供的详细描述。

此外,如前所述,使一个参数构造器(包括那些具有arg2,arg3,...的默认值的构造器)始终是一种好的编码实践。
像往常一样使用C ++:如果我们不这样做,希望我们会...

类的另一种好的做法是将副本构造和赋值设置为私有(也就是将其禁用),除非我们确实需要实现它。这样可以避免在使用C ++默认为我们创建的方法时拥有最终的指针副本。实现此目的的另一种方法是从boost :: noncopyable派生。