6

我不明白怎么constructors work办?

在这里,我声明了一个 object obj2。它调用构造函数abc(),这非常好。

但是当我分配

obj2 =  100 

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

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

我还有一个疑问是,编译器是这样的not doing anythingdefault constructor那么为什么默认构造函数是required

class abc{
public:
    int a, b;

    abc()
    {a = 0; b = 0;}

    abc(int x)
    {a = x;}

    ~abc()
    {std::cout << "Destructor Called\n";}
};
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
4

5 回答 5

7

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

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

obj2 = 100;

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

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

explicit abc(int x) {
    //do something
}
于 2013-09-27T18:09:16.393 回答
5
 obj2 = 100;

你定义了一个构造函数,它接受一个int. 这允许从 int 到abc. 这需要创建一个新对象。它不只是通过调用构造函数神奇地在现有对象中设置一个字段;构造函数构造新对象。

编辑:来自@Steve Jessop 的正确事件顺序

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

于 2013-09-27T18:09:14.230 回答
4

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

#include <iostream>

class abc{
public:
    int a, b;

    abc()
    { std::cout << "Default constructor\n"; a = 0; b = 0;}

    abc(int x)
    { std::cout << "Int constructor\n"; 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 Called\n";}
};

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));,即:

  • 建立一个临时的(未命名的abcabc(100)
  • 分配给obj2
  • 在继续下一条语句之前销毁临时

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

int main () {
  {
    // your code here
  }
  system("pause"); 
  return 0;
}
于 2013-09-27T18:29:16.213 回答
1

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

于 2013-09-27T18:10:49.920 回答
1

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

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

于 2013-09-27T18:09:04.317 回答