构造函数的显式关键字的使用

时间:2016-03-30 13:54:06

标签: c++ class constructor explicit

我试图理解c ++中显式关键字的用法,并在SO上查看了这个问题 What does the explicit keyword mean in C++?

然而,那里列出的例子(实际上两个答案都是前两个)对于用法并不十分清楚。例如,

// classes example
#include <iostream>
using namespace std;

class String {
public:
    explicit String(int n); // allocate n bytes to the String object
    String(const char *p); // initializes object with char *p
};

String::String(int n)
{
    cout<<"Entered int section";
}

String::String(const char *p)
{
    cout<<"Entered char section";
}

int main () {

    String mystring('x');
    return 0;
}

现在我已经将String构造函数声明为显式,但是如果我将构造函数调用为,则可以说如果我不将其列为显式,

String mystring('x');

OR

String mystring = 'x';

在这两种情况下,我都会进入int部分。除非我指定值的类型,否则它默认为int。即使我使用参数更具体,例如将一个声明为int,另一个声明为double,而不是使用显式构造函数名称并以此方式调用

String mystring(2.5);

或者这样

String mystring = 2.5;

它总是默认为带有double参数的构造函数。所以,我很难理解显式的真实用法。你能给我一个例子吗?不使用显式将是一个真正的失败?

1 个答案:

答案 0 :(得分:7)

explicit旨在阻止隐式转换。只要您使用String(foo);之类的内容,就可以进行显式转换,因此使用explicit无法改变是成功还是失败。

因此,让我们看一下涉及隐式转换的场景。让我们从你的String课程开始:

class String {
public:
    explicit String(int n); // allocate n bytes to the String object
    String(const char *p); // initializes object with char *p
};

然后让我们定义一个接收String类型参数的函数(也可能是String const &,但String暂时会这样做:

int f(String);

您的构造函数允许从char const *隐式转换,但只允许从int进行显式转换。这意味着如果我打电话:

f("this is a string");

...编译器将生成代码以从字符串文字构造String对象,然后使用该f对象调用String

但是,如果您尝试致电:

f(2);

它将失败,因为带有String参数的int构造函数已标记为explicit。这意味着如果我想将int转换为String,我必须明确地执行此操作:

f(String(2));

如果String(char const *);构造函数也标记为explicit,那么您也无法调用f("this is a string") - 您必须使用{{1} }}

但请注意,f(String("this is a string"));仅控制从某种类型explicit到您定义的类型的隐式转换。它对从foo构造函数所采用的类型的隐式转换没有影响。因此,采用类型explicit的显式构造函数仍将采用浮点参数:

int

...因为这涉及从f(String(1.2)) double的隐式转换,然后是从intint的显式转换。如果您要禁止从String转换为double,请执行此操作(例如)提供一个带String的重载构造函数,但随后抛出:< / p>

double

现在从String(double) { throw("Conversion from double not allowed"); } double的隐式转换不会发生 - int将直接传递给您的ctor而不进行转换。

关于使用double完成的内容:使用explicit的主要目的是阻止代码编译,否则编译。当与重载相结合时,隐式转换有时会导致一些相当奇怪的选择。

使用转换运算符而不是构造函数来演示问题会更容易(因为您只能使用一个类来实现)。例如,让我们考虑一个很小的字符串类,它与人们在理解隐式转换有多大问题之前编写的很多相似:

explicit

(我已经通过使用class Foo { std::string data; public: Foo(char const *s) : data(s) { } Foo operator+(Foo const &other) { return (data + other.data).c_str(); } operator char const *() { return data.c_str(); } }; 来存储数据而作弊,但如果我确实喜欢这样做并存储std::string并使用char *,那么情况也是如此。分配内存。)

现在,这使得这样的工作正常:

new

......而且,(当然)结果就是打印出Foo a("a"); Foo b("b"); std::cout << a + b; 。但是,如果用户犯了一个小错误并在ab键入-,那么会发生什么?

那些事情变得丑陋 - 代码仍在编译中,并且&#34;工作&#34; (对于这个词的某些定义),但打印出废话。在我的机器上进行快速测试时,我得到+,但不要指望重复该特定结果。

此处的问题源于允许从-24String的隐式转换。当我们尝试减去两个char *对象时,编译器会试图找出我们的意思。由于它无法直接减去它们,因此它会查看是否可以将它们转换为某种支持减法的类型 - 果然,String支持减法,因此它会转换我们的char const *个对象到String,然后减去两个指针。

如果我们将转化标记为char const *

explicit

...试图减去两个explicit operator char const *() { return data.c_str(); } 对象的代码只是简单地编译了。

同样的基本思想可以/确实适用于String构造函数,但是演示它的代码变得更长,因为我们通常至少需要涉及几个不同的类。