9

我知道我们可以同时调用构造函数的概念,Explicitly并且Implicitly我已经测试了这两个场景(通常到现在我的所有目的都通过调用构造函数来实现Implicitly),但我想知道每当我们创建时构造函数都会被隐式调用objects,所以什么是调用构造函数的主要原因Explicitly。当我们调用构造函数而不是它时,它提供了什么优点或Explicitly缺点Implicit Call

例子

class integer
{
   int m ,n;
 public:
   integer (int x , int y); 
};
integer :: integer (int x , int y )
{
   m=x; n = y;
}

现在如果我打电话

integer int1 = integer( 0 , 100); //  Explicit Call
integer int1(1,100); // implicit call
4

5 回答 5

20

这里有两个不同的问题,因为您对显式隐式的定义与标准定义不匹配(大多数现有答案都基于标准定义,在您添加包含您自己的显式隐式定义的示例之前编写)。

好的,让我们首先考虑一下你对explicit的定义,这将是(我猜你称它为explicit是因为你明确地写了类型名称?):

integer int1 = integer(0, 100);

与您对隐式的定义相比:

integer int1(1, 100);

在这种情况下,第一个“显式”调用确实比第二个“隐式”调用没有任何优势。但是还是有区别的。第一个实际上使用两个参数的构造函数创建了一个临时对象,然后使用int1复制构造函数进行创建。尽管实际上编译器通常会优化掉这个额外的副本,但如果您的副本构造函数是私有的,它仍然无法工作,而第二个只需要双参数构造函数(您甚至可以将此视为缺点)。


但现在到了显式隐式的实际标准定义。显式构造函数调用是任何构造函数调用,好吧,显式调用。实际上,每当您使用括号语法()创建对象时,您都会显式调用构造函数,否则它是隐式构造函数调用(也就是说,由编译器在幕后完成):

integer int1;                   // implicit default constructor
integer int1(1, 100);           // explicit two-arg constructor
integer int1 = integer(0, 100); // explicit two-arg constructor, implicit copy constructor

void func(integer);             // function taking by-value
func(int1);                     // implicit copy constructor

因此,唯一可以隐式调用的构造函数是默认构造函数和任何单参数构造函数(包括复制和移动构造函数)。这方面的一个特殊问题是单参数构造函数不是复制/移动构造函数:

struct integer
{
    integer(int);
};

这允许编译器式调用构造函数来转换类型,因此 anyint可以隐式转换为integer

void func(integer);
func(42);             // implicit call to int-constructor

要禁止这种行为,您必须标记构造函数explicit

struct integer
{
    explicit integer(int);
};

这只允许显式调用它(例如func(integer(42)))(但我猜你已经知道这一点)。这样做的好处是它不会在幕后引入未被注意到/不需要的转换,这可能会导致各种难以发现的问题和关于重载解决方案的歧义。因此,通常的做法是标记任何转换构造函数(单参数非复制/移动构造函数)explicit,并且很可能也是 C++11 最终引入explicit转换运算符的原因。


integer int1 = integer(1, 100);因此,总而言之,根据您的定义和示例,使用instead of确实没有任何优势integer int1(1, 100);,尽管它会产生(通常不相关的)差异。

但是根据标准定义,显式构造函数调用比隐式调用有很多优势,因为显式构造对象的唯一方法是使用显式构造函数调用,而隐式构造函数调用仅在某些情况下在幕后完成情况,并且仅适用于零参数和一参数构造函数(正如aschepler已经指出的那样)。并且将转换构造函数显式标记为explicit具有不允许在幕后进行不需要的隐式转换的优点。

于 2012-08-20T13:21:33.263 回答
2

显式调用构造函数允许您使用参数构造对象,而不是使用默认构造函数

class Foo
{
  public:
    Foo() {}
    Foo(int bar) : mBar(bar) {}
  private:
    int mBar;
}

Foo f;    // Implicitly constructed with default constructor.
Foo f(7); // Explicitly constructed with argument for 'bar'
于 2012-08-20T10:32:26.840 回答
2

可以通过三种方式调用构造函数:

  • 隐式地,通过声明类型的实例而不初始化它
  • =同样隐式地,通过使用或通过导致从参数类型到您的类的隐式转换来初始化实例。
  • 显式调用构造函数,传递参数。

您可以在特定上下文中使用哪些取决于您正在调用的构造函数。

class Foo 
{
    Foo();                                  // 1
    Foo(int a);                             // 2
    explicit foo(const std::string& f);     // 3
    Foo(int c, int d);                      // 4
};
  1. 声明时将隐式调用此构造函数Foo f;永远不要尝试显式调用没有参数的构造函数,就像Foo f();声明一个函数一样!
  2. 这个可以通过写作Foo f = 42;或来调用Foo f(42)
  3. 关键字通过写orexplicit禁止隐式转换。Foo f = std::string("abc");function_taking_foo(function_returning_string());
  4. 由于有多个参数,显式版本是唯一合适的。
于 2012-08-20T10:42:17.787 回答
1

我讨厌这样说,因为它太反常了,但是还有另一种方法可以显式调用构造函数。

class integer
{
   int m ,n;
 public:
   integer (int x , int y); 
};
integer :: integer (int x , int y )
{
   m=x; n = y;
}

可以在已构造的对象上显式调用构造函数。

integer i(1,100);
i.~integer();
i.integer::integer(2,200);

在这里,我(明确地)构造了一个整数实例。然后我明确地调用了它的析构函数。然后我再次明确地调用了构造函数。我想你可能会在测试中使用这个成语。我不知道标准中有任何地方禁止它。它适用于 Visual Studio 2010。我还没有测试过非常广泛的编译器。

这些调用对于“显式”的大值是显式的。

于 2015-09-02T19:02:28.313 回答
0

如果你创建一个函数来引用你的类的一个对象,并且你将它传递给你的对象以外的另一种类型,那么你的类的构造函数会将该类型转换为你的类的对象。任何一个参数构造函数都被视为转换构造函数。如果您显式声明该构造函数,则将对象以外的其他类型传递给该函数将不会对其进行转换,并且编译器将返回错误

于 2012-08-20T10:39:15.703 回答