提问人:Julian Popov 提问时间:5/17/2010 最后编辑:Julian Popov 更新时间:11/8/2023 访问量:5678
构造函数或赋值运算符
Constructor or Assignment Operator
问:
你能帮我吗 C++ 标准中有定义描述在这种情况下哪一个将被称为构造函数或赋值运算符:
#include <iostream>
using namespace std;
class CTest
{
public:
CTest() : m_nTest(0)
{
cout << "Default constructor" << endl;
}
CTest(int a) : m_nTest(a)
{
cout << "Int constructor" << endl;
}
CTest(const CTest& obj)
{
m_nTest = obj.m_nTest;
cout << "Copy constructor" << endl;
}
CTest& operator=(int rhs)
{
m_nTest = rhs;
cout << "Assignment" << endl;
return *this;
}
protected:
int m_nTest;
};
int _tmain(int argc, _TCHAR* argv[])
{
CTest b = 5;
return 0;
}
还是只是编译器优化的问题?
答:
在这种情况下,它始终是构造函数采用调用的构造函数。这称为隐式转换,在语义上等同于以下代码:int
CTest b(5);
赋值运算符永远不会在初始化中调用。请考虑以下情况:
CTest b = CTest(5);
在这里,我们显式调用构造函数(取一个),然后使用该临时值来初始化 .但同样,从未调用过赋值运算符。int
b
严格来说,在 C++17 之前,这两种情况都可以在创建 类型的对象后调用复制(或移动)构造函数。但事实上,C++ 标准积极鼓励编译器优化复制构造函数调用这里1,并且所有编译器都优化了几十年。CTest
从 C++17 开始,上面的代码不再调用复制构造函数:即使代码看起来仍然像是执行了复制(或移动),值 的值直接构造到 的存储中,不会创建临时的。阿拉伯数字CTest(5)
b
1 参见 ISO/IEC 14882:1998 [class.copy]/15。这被称为复制 elison(特别是 URVO,它令人困惑地代表“未命名的返回值优化”,尽管它也适用于不是返回语句的初始化表达式)。
2 参见 ISO/IEC 14882:2017 [dcl.init]/17.6.1(工作草案 N4659)。
评论
CTest b = CTest(5);
a + b - b
a
这里发生的事情有点取决于你的编译器。它可以使用 int 构造函数创建一个临时对象,然后从该临时对象复制构造 b。但是,它很可能会省略复制构造函数调用。在这两种情况下,都不会使用赋值运算符。
评论
CTest b = 5;
是完全等价的 涉及两个构造函数:一个采用一个(隐式从整数 5 转换),另一个采用复制构造函数。此处不涉及赋值运算符。CTest b(CTest(5));
int
编译器可能会优化不必要的副本,因此结果就像您键入了 .因此,在运行时,看到打印的“复制构造函数”(带有选项的 GCC)或不打印(默认为 GCC)都是程序的有效输出。CTest b(5)
-fno-elide-constructors
但是,从概念上讲,编译器需要检查是否存在可访问且合适的复制构造函数。如果 a) 复制构造函数是私有的/受保护的(不可访问)或 b) 复制构造函数通过非常量引用获取参数(不能接受临时来自 - VC++ 可能会接受它作为非标准编译器扩展,则表单将无法编译。CTest b = 5;
CTest(5)
士气是:没有简单的方法可以通过查看代码来判断复制构造函数在程序中被调用的位置和次数。复制通常可以省略,因此永远不应该依赖复制构造函数的副作用。如果它做了它应该做的事情,那么编译器是否消除了一些不必要的复制构造函数调用对你来说应该无关紧要。
评论