构造函数被调用的次数
Posted
技术标签:
【中文标题】构造函数被调用的次数【英文标题】:Number of times a constructor is called 【发布时间】:2019-01-10 00:26:55 【问题描述】:假设我们有一个自定义字符串类,如下所示
using namespace std;
class CustomStr
public:
const char* s;
// converting constructor
CustomStr(const char* s) : s (s)
cout << "Constructor called" << endl;
// Copy constructor
CustomStr(const CustomStr& cs) : s (cs.s)
cout << "Copy Constructor called" << endl;
;
让我们考虑以下代码:
int main()
CustomStr cs("Some char pointer");
CustomStr cs_copy = cs;
return 0;
在上面的例子中,对于CustomStr cs_copy = cs
,我们希望先调用转换构造函数,然后调用复制构造函数,我们得到了预期的相应输出
$ ./a.out
Constructor called
Copy Constructor called
现在,考虑下面的代码
int main()
CustomStr cs_copy = CustomStr("Some char pointer");
return 0;
在这种情况下,我也认为应该进行 2 个构造函数调用 - 为 CustomStr("Some char pointer")
转换构造函数调用,为 CustomStr cs_copy = CustomStr("Some char pointer")
调用复制构造函数。但是,输出显示只调用了转换构造函数。
我无法解释这是怎么发生的。我希望它对于编译器进行优化来说是一个容易实现的成果 - 但我想了解这种优化在什么情况下起作用。
【问题讨论】:
是的,这是编译器优化。在最近的 C++ 修订版中,何时允许这种优化和其他类型的优化的规则已经改变了——有时是相当大的。在某些情况下,允许编译器优化复制构造或复制分配,即使它有副作用。在某些情况下,它甚至是必需的。不必担心这一点——遵守规则。不要在特定情况下对复制构造函数或发生的复制赋值引入脆弱的依赖,让编译器担心生成正确的代码。 注意“默认构造函数”是一个技术术语,它不描述您的第一个构造函数。通常意义上的“默认构造函数”是可以与零参数一起使用的构造函数。所以CustomStr();
或CustomStr(const char* s = nullptr);
都是默认构造函数。
@aschepler - 感谢您指出这一点。我已经相应地编辑了问题
【参考方案1】:
引用cppreference:
如果初始化器是一个纯右值表达式,其类型与 T 相同(忽略 cv 限定),初始化器表达式本身,而不是从它的临时物化,用于初始化目标对象
CustomStr("Some char pointer")
是与cs_copy
相同类型的纯右值。因此没有临时也没有副本。 cs_copy
只是直接从该表达式初始化。
在 C++17 之前,就抽象机而言,曾经有一个临时的,但标准不要求调用复制构造函数,因此与 C++17 具有相同的行为被允许作为优化。更多详情请参见 cppreference:copy elision。
【讨论】:
"默认构造函数不带参数" - 更准确地说,它们可以带参数,只要它们有默认值。没有指定参数值的默认构造函数是 CALLED。例如:CustomStr(const char* s = NULL)
是一个有效的默认构造函数和一个转换构造函数。
@RemyLebeau 我把句子改正为不正确。
@eerorika - 我已编辑问题以删除“默认构造函数”并使用“转换构造函数”
@ShauryaKumar 我删除了过时的帖子脚本。【参考方案2】:
自 C++17 起,T x = T(A);
的定义与 T x(A);
的含义完全相同。 (其中A
是任何可能为空的表达式列表,并且没有 MVP)。
在 C++17 之前,允许编译器选择是否将代码处理为 T x(A);
,或者是否构造一个临时的,然后从该临时复制/移动构造 x
。
【讨论】:
以上是关于构造函数被调用的次数的主要内容,如果未能解决你的问题,请参考以下文章