析构函数是否删除了正确的实例。

huangapple go评论58阅读模式
英文:

Is the destructor deleting the correct instance

问题

I am using Visual C++ in the Community edition of Visual Studio 2022 with the latest updates. In the C++ code below, main creates an instance of ShowBug and assigns it to the variable sb. The next line creates a 2nd instance of ShowBug and also assigns it to sb.

在使用 Visual Studio 2022 的 Community 版本进行最新更新的 Visual C++ 时,以下是C++代码。在主函数中,创建了一个 ShowBug 实例并将其赋给变量 sb。接下来的一行创建了第二个 ShowBug 实例,也将其赋给 sb。

Before the 2nd line completes, it calls the destructor. But, it does not destruct the first instance, which is what I thought it would do, but instead destructs the newly created 2nd instance. Try it yourself if you find it hard to believe.

在第二行完成之前,它调用了析构函数。但是,它没有销毁第一个实例,这与我之前的想法不符,而是销毁了新创建的第二个实例。如果你觉得难以置信,可以自行尝试。

So, am I missing something here (i.e. is using the same variable to assign a new instance a bad programming practice?), or is the compiler somehow doing the correct thing? Or, is this a compiler bug?

那么,我是否遗漏了什么(即使用相同的变量来分配一个新实例是否是一种不好的编程实践?),或者编译器是否以某种方式执行了正确的操作?或者,这是编译器的错误?

英文:

I am using Visual C++ in the Community edition of Visual Studio 2022 with the latest updates. In the C++ code below, main creates an instance of ShowBug and assigns it to the variable sb. The next line creates a 2nd instance of ShowBug and also assigns it to sb.

Before the 2nd line completes, it calls the destructor. But, it does not destruct the first instance, which is what I thought it would do, but instead destructs the newly created 2nd instance. Try it yourself if you find it hard to believe.

So, am I missing something here (i.e. is using the same variable to assign a new instance a bad programming practice?), or is the compiler somehow doing the correct thing? Or, is this a compiler bug?

// ShowBug.h:

using namespace std;
#include <iostream>
#include <string>

class ShowBug
{
   // This class is used to show that the destructor seems to be called for the wrong instance.
   //
   string S;
   int *pArray;
public:
   inline ShowBug(const string &s) : S(s)
   {
   }

   inline ~ShowBug()
   {
      std::cout << "Deleting " + S;
   }
};

int main()
{
   ShowBug sb = ShowBug("First Instance");
   sb = ShowBug("Second Instance");
}

</details>


# 答案1
**得分**: 5

在你执行以下代码时:

sb = ShowBug("Second Instance");

表达式`ShowBug(&quot;Second Instance&quot;)`创建了一个*临时*对象,类型为`ShowBug`。一旦完整表达式(赋值)结束,这个临时对象将会立即被销毁。

当然,这会导致问题,因为它的销毁也会释放分配的内存。指针会被赋值,但**只有**指针本身,而不是指向的内存。

所以,在赋值之后,指针`sb.pArray`将不再有效。除了使用错误的`delete`运算符外,尝试再次`delete`无效指针会导致*未定义行为*。

解决办法是遵循[三五零法则](https://en.cppreference.com/w/cpp/language/rule_of_three)之一。我推荐使用零法则,通过使用`std::vector&lt;int&gt;`或者在你的情况下使用`std::array&lt;int, 3&gt;`,而不是现在所用的动态分配。然后你可以移除析构函数。

如果你必须使用指针和动态分配,那么你至少需要遵循三法则,并且实现一个拷贝构造函数和一个拷贝赋值运算符。

---

我还忽略了初始化:

ShowBug sb = ShowBug("First Instance");

也有相同的问题:你创建了一个临时对象,它用于拷贝构造`sb`对象。然后,临时对象被销毁,带走了内存和原始指针。

然而,解决办法仍然相同:三法则、五法则或零法则。

<details>
<summary>英文:</summary>

When you do

sb = ShowBug("Second Instance");

the expression `ShowBug(&quot;Second Instance&quot;)` creates a *temporary* object of type `ShowBug`. This temporary object will be immediately destructed once the full expression (the assignment) is finished.

This of course leads to problems, as its destruction also free the memory allocated. The pointer will be copied by the assignment, but *only* the pointer itself and not the memory it points to.

So after the assignment the pointer `sb.pArray` will no longer be valid. Besides the use of the wrong `delete` operator, attempting to `delete` the invalid pointer again leads to *undefined behavior*.

The solution is to follow one of [the rules of three, five or zero](https://en.cppreference.com/w/cpp/language/rule_of_three). I recommend the rule of zero, by using a `std::vector&lt;int&gt;` or in your case `std::array&lt;int, 3&gt;` instead of the dynamic allocation you do now. Then you can remove the destructor.

If you must use pointers and dynamic allocations, then you need to follow at least the rule of three, and implement a copy-constructor as well as a copy-assignment operator.

---

I also overlooked that the initialization

ShowBug sb = ShowBug("First Instance");

have the exact same problem: You create a temporary object, which is used to copy-construct the `sb` object. The temporary object is then destructed, taking the memory and original pointer with it.

The solution is still the same though: The rule of zero, three or five.

</details>



huangapple
  • 本文由 发表于 2023年5月15日 10:35:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/76250560.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定