San*_*996 10 c++ constructor explicit class
我试图理解c ++中显式关键字的用法,并在SO上查看了这个问题. 显式关键字在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;
}
Run Code Online (Sandbox Code Playgroud)
现在我已经将String构造函数声明为显式,但是如果我不将它列为显式,如果我将构造函数称为,
String mystring('x');
Run Code Online (Sandbox Code Playgroud)
要么
String mystring = 'x';
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,我都会进入int部分.除非我指定值的类型,否则它默认为int.即使我使用参数更具体,例如将一个声明为int,另一个声明为double,而不是使用显式构造函数名称并以这种方式调用它
String mystring(2.5);
Run Code Online (Sandbox Code Playgroud)
或者这样
String mystring = 2.5;
Run Code Online (Sandbox Code Playgroud)
它总是默认为带有double参数的构造函数.所以,我很难理解显式的真实用法.你能给我一个例子吗?不使用显式将是一个真正的失败?
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
};
Run Code Online (Sandbox Code Playgroud)
然后让我们定义一个接收类型参数的函数String(也可以String const &,但String目前会这样做):
int f(String);
Run Code Online (Sandbox Code Playgroud)
您的构造函数允许隐式转换char const *,但只允许显式转换int.这意味着如果我打电话:
f("this is a string");
Run Code Online (Sandbox Code Playgroud)
...编译器将生成代码以从字符串文字构造String对象,然后f使用该String对象调用.
但是,如果您尝试致电:
f(2);
Run Code Online (Sandbox Code Playgroud)
它将失败,因为已经标记String了带int参数的构造函数explicit.这意味着如果我想将inta 转换为a String,我必须明确地做到:
f(String(2));
Run Code Online (Sandbox Code Playgroud)
如果String(char const *);构造函数也被标记explicit,那么你也无法调用f("this is a string")- 你必须使用f(String("this is a string"));
但请注意,explicit仅控制从某种类型foo到您定义的类型的隐式转换.它从一些其他类型的隐式转换没有影响到你的类型explicit的构造需要.因此,采用类型的显式构造函数int仍将采用浮点参数:
f(String(1.2))
Run Code Online (Sandbox Code Playgroud)
...因为这涉及从隐式转换double到int后跟显式转换int为String.如果你想禁止转换double为String,你可以通过(例如)提供一个重载的构造函数来获取double,然后抛出:
String(double) { throw("Conversion from double not allowed"); }
Run Code Online (Sandbox Code Playgroud)
现在从隐式转换double到int不会发生-在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(); }
};
Run Code Online (Sandbox Code Playgroud)
(我通过使用std::string来存储数据而作弊,但如果我喜欢它们并且存储了一个char *并且用于new分配内存,那么情况也是如此).
现在,这使得这样的工作正常:
Foo a("a");
Foo b("b");
std::cout << a + b;
Run Code Online (Sandbox Code Playgroud)
......(当然)结果是打印出来ab.但是,如果用户犯了一个小错误并-输入他们打算输入的内容,会发生什么+?
这就是事情变得丑陋的地方 - 代码仍在编译和"工作"(对于单词的某些定义),但打印出废话.在我的机器上进行快速测试,我得到了-24,但不要指望重复那个特定的结果.
这里的问题源于允许隐式转换String为char *.当我们尝试减去这两个String对象时,编译器会试图找出我们的意思.由于它不能直接减去它们,它会查看它是否可以将它们转换为某种支持减法的类型 - 当然,它char const *支持减法,因此它将我们的String对象转换为char const *,然后减去两个指针.
如果我们将转换标记为explicit:
explicit operator char const *() { return data.c_str(); }
Run Code Online (Sandbox Code Playgroud)
...试图减去两个String对象的代码根本无法编译.
同样的基本思想可以/确实适用于explicit构造函数,但是演示它的代码变得更长,因为我们通常至少需要涉及几个不同的类.