为什么构造函数被调用两次

Why constructor is being called twice

本文关键字:两次 构造函数 调用 为什么      更新时间:2023-10-16

我不明白怎么constructors work

在这里,我声明了一个对象obj2。它调用构造函数abc(),这完全没问题。

但是当我分配时

obj2 =  100 

为什么编译器允许将整数初始化为类对象?如果它允许,那么它如何销毁对象,然后它如何调用另一个参数化构造函数。

现在我有另一个问题,为什么destructor因为有two对象而只调用一次?

我还有一个疑问是,编译器与default constructor not doing anything,那么为什么默认构造函数是required

class abc{
public:
    int a, b;
    abc()
    {a = 0; b = 0;}
    abc(int x)
    {a = x;}
    ~abc()
    {std::cout << "Destructor Calledn";}
};
int main()
{
    abc obj1;
    cout << "OBJ1 " << obj1.a << "..." << obj1.b << "n";
    abc obj2;
    cout << "OBJ2 " << obj2.a << "..." << obj2.b << "n";
    obj2 = 100;
    cout << "OBJ2 " << obj2.a << "n";
system("pause");
return 0;
}

output:

OBJ1 0...0
OBJ2 0...0
Destructor Called
OBJ2 100

但是当我分配 obj2 = 100 时,编译器如何允许将整数初始化为类对象?

这是因为当您执行以下操作时:

obj2 = 100;

这将首先调用 abc(int x) 来生成类的对象,然后调用默认的复制赋值运算符(因为没有提供用户定义的(将值 100 分配给现有obj2。分配后,临时对象将被销毁。

如果不希望产生这种效果,请将构造函数标记为explict以避免隐式调用。

explicit abc(int x) {
    //do something
}
 obj2 = 100;

您定义了一个采用int的构造函数。 这允许从 int 到 abc 的隐式转换。 这需要创建一个新对象。 它不只是通过调用构造函数神奇地在现有对象中设置字段;构造函数构造新对象。

编辑:来自@Steve杰索普的正确事件顺序

创建一个新实例,然后将副本分配给原始实例,然后销毁临时(而不是原始实例(。复制赋值确实神奇地在现有对象中设置了这两个字段。

让我们表演和讲述,让我们为所有特殊成员进行乐器:

#include <iostream>
class abc{
public:
    int a, b;
    abc()
    { std::cout << "Default constructorn"; a = 0; b = 0;}
    abc(int x)
    { std::cout << "Int constructorn"; a = x;}
    abc(abc const& other): a(other.a), b(other.b)
    { std::cout << "Copy constructor (" << a << ", " << b << ")n"; }
    abc& operator=(abc const& other) {
      std::cout << "Assignment operator (" << a << ", " << b << ") = (" << other.a << ", " << other.b << ")n";
      a = other.a;
      b = other.b;
      return *this;
    }
    ~abc()
    {std::cout << "Destructor Calledn";}
};
int main()
{
    abc obj1;
    std::cout << "OBJ1 " << obj1.a << "..." << obj1.b << "n";
    abc obj2;
    std::cout << "OBJ2 " << obj2.a << "..." << obj2.b << "n";
    obj2 = 100;
    std::cout << "OBJ2 " << obj2.a << "n";
    return 0;
}

我们得到这个输出:

Default constructor
OBJ1 0...0
Default constructor
OBJ2 0...0
Int constructor
Assignment operator (0, 0) = (100, 0)
Destructor Called
OBJ2 100
Destructor Called
Destructor Called

因此,让我们将它们与行源进行协调:

int main()
{
    abc obj1;
    // Default constructor
    std::cout << "OBJ1 " << obj1.a << "..." << obj1.b << "n";
    // OBJ1 0...0
    abc obj2;
    // Default constructor
    std::cout << "OBJ2 " << obj2.a << "..." << obj2.b << "n";
    // OBJ2 0...0
    obj2 = 100;
    // Int constructor
    // Assignment operator (0, 0) = (100, 0)
    // Destructor Called
    std::cout << "OBJ2 " << obj2.a << "n";
    // OBJ2 100
    return 0;
    // Destructor Called
    // Destructor Called
}

你基本上拥有了一切,让我们检查一下惊喜。

第一个惊喜:即使obj2稍后更改值abc obj2;仍将在声明点调用默认构造函数。

第二个惊喜:obj2 = 100其实是obj2.operator=(abc(100));的意思,即:

  • abc(100)构建临时(未命名(abc
  • 将其分配给obj2
  • 在继续下一条语句之前销毁临时语句

第三个惊喜:析构函数在范围的末尾调用,就在右括号}之前(是的,在return之后(。由于您使用的是system("pause")我假设您在 Windows 上 =>尽管幸运的是它们在您结束暂停后被调用,因此您的控制台 Windows 在它们出现的那一刻眨眼间消失了。您可以从更永久的控制台启动程序,也可以使用额外的作用域:

int main () {
  {
    // your code here
  }
  system("pause"); 
  return 0;
}

这是因为有构造函数可以接受int类型的参数。通过调用默认复制分配,将临时创建的对象复制到obj2

若要避免此类转换,请将构造函数标记为显式。

你的析构函数被调用了 3 次,由于暂停,你看不到它。