我在 C++ 中遇到两次调用析构函数的问题

Im Facing a Problem With Destructor Called Twice in C++

编辑:我不得不使用 unique_ptr 或遵循五法则我仍在学习它所以我使用 unique_ptr 并且它起作用了。但是我有一个问题,现在析构函数被调用了两次,我认为只要指针指向的内存块没有被释放两次就没有问题,对吗??

我用 c++ 做了一个简单的“String”class(从 The Cherno 知道)。所以我做到了,它似乎工作得很好,直到我决定添加一个空的构造函数以便能够在没有参数的情况下初始化它但是当我这样做时,析构函数被调用了两次。

// here is the header file
#pragma once
#include <iostream>

using namespace std;

class String
{
private:
    //Hold the raw of chars in the heap memory
    char* m_Buffer;
    //The size of the buffer in heap
    unsigned int m_Size;
public:
    //An empty Constructor
    String()
        : m_Buffer(nullptr), m_Size(0)
    {
        cout << "created Empty." << endl;
    }

    // A Constructor
    String(const char* string)
    {
        m_Size = strlen(string);
        m_Buffer = new char[m_Size + 1];
        memcpy(m_Buffer, string, m_Size + 1);
        m_Buffer[m_Size] = 0;
    }

    // A destructor
    ~String()
    {
        cout << "Destroy!!" << endl;
        

        delete[] m_Buffer;
    }

    // Function resposable for coping
    String(const String& other)
        : m_Size(other.m_Size)
    {
        m_Buffer = new char[m_Size + 1];
        memcpy(m_Buffer, other.m_Buffer, m_Size + 1);
    }
    

    char& operator[](unsigned int& index)
    {
        return m_Buffer[index];
    }


        friend std::ostream& operator<<(std::ostream& stream, const String& other);
    };

    std::ostream& operator<<(std::ostream& stream, const String& other)
    {
        stream << other.m_Buffer << endl;
        return stream;
    }

    


       //here is the main file

    #include "LclString.h"
    int main()
    
    {
        String a = "asdc";
    
        a = "ads"; // The Destructor is being called here the first time 
        
        cin.get();
    } // and here is the second time

如有建议,将不胜感激 .....................................

您的析构函数调用 delete[] m_Buffer。一个指针可能永远不会被删除两次。如果确实删除了一个指针两次,那么程序的行为将是未定义的。你必须避免这样做。为避免这种情况,您必须确保 class 的两个实例在成员中没有相同的指针值。

考虑一下 class 的隐式生成的赋值运算符的作用。它将所有成员从右手操作数复制到左手操作数。你能看出为什么这是个问题吗?所有成员都包含 m_Buffer 指针。赋值运算符将导致 class 的两个实例具有相同的指针。当第二个实例被销毁时,它再次删除同一个指针。并且程序的行为是未定义的。

还有一个相关的问题,隐式赋值运算符覆盖了旧的m_Buffer。谁来删除被覆盖的指针?没有人会删除它,因为赋值丢失了值。那是内存泄漏。

结论:

  • 避免使用拥有裸指针。
  • 避免直接使用new和delete。
  • 如果您有用户定义的析构函数,那么您可能还需要用户定义的 copy/move 构造函数和赋值运算符。这被称为 5 法则(以前是 3 法则)。如果您使用智能指针而不是拥有裸指针,那么您通常不需要用户定义的析构函数。