我知道我们可以同时调用构造函数Explicitly
和Implicitly
的概念,并且我已经测试了两个场景(通常到现在为止,通过调用构造函数Implicitlly
实现了我的所有目的),但我想知道每当我们创建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
答案 0 :(得分:15)
这里有两个不同的问题,因为您对显式和隐式的定义与标准定义(大多数现有答案所基于的,正在编写)不匹配在添加包含您自己的显式和隐式定义的示例之前。
好的,让我们首先考虑你对显式的定义,这是(我猜你称之为显式,因为你明确地写了类型名称?):
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);
};
这允许编译器隐式调用构造函数来转换类型,因此任何int
都可以隐式转换为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);
代替integer int1(1, 100);
确实没有优势,尽管它会产生(通常无关紧要)差异。
但是根据标准定义,显式构造函数调用比隐式调用具有更多优势,因为实际构建对象的唯一方法是显式是使用,显式构造函数调用,而隐式构造函数调用仅在某些情况下在幕后完成,仅适用于零参数和单参数构造函数(正如 aschepler 已经指出的那样)。明确地将转换构造函数标记为explicit
具有在幕后禁止不必要的隐式转换的优势。
答案 1 :(得分: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'
答案 2 :(得分:2)
可以通过三种方式调用构造函数:
=
初始化实例或通过导致从参数类型到类的隐式转换。您可以在特定上下文中使用哪些内容取决于您正在调用的构造函数。
class Foo
{
Foo(); // 1
Foo(int a); // 2
explicit foo(const std::string& f); // 3
Foo(int c, int d); // 4
};
Foo f;
时将隐式调用此构造函数。 从不尝试显式调用没有参数的构造函数,因为Foo f();
将声明一个函数! Foo f = 42;
或Foo f(42)
来调用此作品。explicit
关键字通过撰写Foo f = std::string("abc");
或function_taking_foo(function_returning_string());
来禁止隐式转化。答案 3 :(得分: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.我还没有测试过各种各样的编译器。
这些调用对于'explicit'的大值是明确的。
答案 4 :(得分:0)
如果你创建了一个引用类对象的函数,并且传递了除对象之外的其他类型,则类的构造函数会将该类型转换为类的对象。任何一个参数构造函数都被视为转换构造函数。如果声明构造函数是显式的,那么将除了对象之外的其他类型传递给该函数将不会转换它,编译器将返回错误