没有默认构造函数的初始化赋值

Assignment at Initialization without Default Constructor

本文关键字:初始化 赋值 构造函数 默认      更新时间:2023-10-16

在这个程序中,我完全理解为什么主函数的第一部分失败了,需要注释——在我在TestingClass中实现了值变量之后,没有隐式的默认变量。完美的逻辑。然而,我有点惊讶地发现第二部分(创建test2对象)非常成功,至少在gcc 4.8.4中是这样。

#include <iostream>
using namespace std;
class TestingClass
{
  public:
    TestingClass(int inVal)
    {
      val = inVal;
    }
    int val;
};
TestingClass testingCreator()
{
  return TestingClass(100);
}
int main()
{
  /*
  TestingClass test1;
  test1 = testingCreator();
  cout << "Test1: " << test1.val << endl;
  */
  TestingClass test2 = testingCreator();
  cout << "Test2: " << test2.val << endl;
}

考虑一下,这也是有道理的,因为对象test2如果没有被构造/初始化就不会存在,但是大多数人认为这样的初始化只是一行的声明和赋值。显然,初始化比这更特殊,因为这段代码可以工作。

这是标准c++吗?它能保证跨编译器工作吗?我感兴趣的是如何初始化以这种方式是不同于只是声明(使用默认的actor),然后分配(通过在全局函数中创建的临时对象)。

UPDATE:添加了一个复制操作符和第三个明显使用复制操作符的case。

#include <iostream>
using namespace std;
class TestingClass
{
  public:
    TestingClass(const TestingClass &rhs)
    {
      cout << "In copy ctor" << endl;
      this->val = rhs.val + 100;
    }
    TestingClass(int inVal)
    {
      val = inVal;
    }
    int val;
};
TestingClass testingCreator()
{
  return TestingClass(100);
}
int main()
{
  /*
  TestingClass test1;
  test1 = testingCreator();
  cout << "Test1: " << test1.val << endl;
  */
  TestingClass test2 = testingCreator();
  cout << "Test2: " << test2.val << endl;
  TestingClass test3(test2);
  cout << "Test3: " << test3.val << endl;
}
这个输出:

Test2: 100
In copy ctor
Test3: 200

UPDATE 2:已经很长时间了,但是碰巧遇到了我的这个老问题,我想我应该添加这个以防其他人在这里结束。如下面的答案所述,正在使用复制构造函数,但省略了对复制函数的实际调用,因此"副作用"(具体地说,是"在复印件上"的印刷);也不执行通过添加100来修改值的操作。为了证明这一点,您可以指定g++命令行参数-fno-elide-constructors,它将防止省略发生。当使用该标志编译时,示例的输出是:

In copy ctor
In copy ctor
Test2: 300
In copy ctor
Test3: 400

一般情况下,您可能不想使用该标志,但在试图理解构造函数省略的幕后发生的事情时,它可能很有用。正如许多关于该主题的引用所指出的那样,这显然是为什么您的复制函数除了复制之外什么都不做的原因-如果使用构造函数省略,则可能不会执行任何其他副作用。在我的示例中,我在复制因子中有两个副作用,并且它们在执行期间没有执行,因此避免复制因子中的副作用是一个很好的建议,因为结果可能会让一些人感到惊讶。

你对TestingClass test2 = testingCreator();的想法是有缺陷的。当你看到

type name = stuff;

不能先创建name,再分配给stuff。你所做的就是从stuff复制初始化name。这意味着调用复制或移动构造函数。一般来说,这个调用可以通过优化编译器来忽略,但如果不是这样,那么这就是您将看到的。在这两种情况下,都不会调用默认构造函数。

在第一个例子中

TestingClass test1;

强制调用默认构造函数,由于您没有默认构造函数,因此会得到一个错误。

test2TestingClass的复制构造函数定义,以testingCreator的结果作为参数。复制构造函数TestingClass::TestingClass(const TestingClass&)由编译器自动生成,c++标准保证它复制val字段。