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

标签 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');


String mystring = 'x';

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

或者这样
String mystring = 2.5;

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

最佳答案

explicit旨在防止隐式转换。任何时候你使用类似 String(foo); 的东西,这是一个显式转换,所以使用 explicit无论成功还是失败都不会改变。

因此,让我们看一个涉及隐式转换的场景。让我们从您的 String 开始类(class):

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 .这意味着如果我想转换 intString ,我必须明确地做到这一点:
f(String(2));

如果String(char const *);构造函数也被标记 explicit ,那么您将无法拨打 f("this is a string")要么——你必须使用 f(String("this is a string"));
但是请注意,explicit仅控制来自某种类型的隐式转换 foo到您定义的类型。它对从其他类型到您的类型的隐式转换没有影响 explicit构造函数需要。因此,您的显式构造函数采用类型 int仍将采用浮点参数:
f(String(1.2))

...因为这涉及到来自 double 的隐式转换至 int后跟从 int 的显式转换至 String .如果您想禁止来自 double 的转换至 String ,您可以通过(例如)提供一个重载的构造函数来实现,该构造函数采用 double ,但随后抛出:
String(double) { throw("Conversion from double not allowed"); }

现在来自 double 的隐式转换至 int不会发生--double将直接传递给您的 ctor,无需转换。

至于用什么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 .但是如果用户犯了一个小错误并输入 - 会发生什么?他们打算在何处输入 + ?

这就是事情变得丑陋的地方——代码仍然可以编译和“工作”(对于这个词的某些定义),但打印出来的是废话。在我的机器上的快速测试中,我得到了 -24 ,但不要指望复制该特定结果。

这里的问题源于允许从 String 进行隐式转换至 char * .当我们尝试减去两个 String 时对象,编译器试图弄清楚我们的意思。既然不能直接减法,就看能不能转换成支持减法的类型——果然,char const *支持减法,所以它转换我们的 String反对 char const * ,然后减去两个指针。

如果我们将该转换标记为 explicit相反:
explicit operator char const *() { return data.c_str(); }

...试图减去两个的代码 String对象根本不会编译。

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

关于c++ - 构造函数的显式关键字的使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36310944/

相关文章:

c++如何使用select查看套接字是否已关闭

c++ - 如何按索引分配对象数组? (C++)

c++ - 使用复制构造函数后双重释放子对象

jquery - 删除/添加链接类 onclick

java - 无法将一个类的对象访问到另一个类中

需要 C# 构造函数语法解释

c++ - 如何使用基类指针引用派生类成员?

c++ - C/C++ pthread 信号和指针

C++ 单独的头文件和源文件

c++ - <function> 不是 <class> 的成员