【问题标题】:Destructor does not destroy objects析构函数不销毁对象
【发布时间】:2013-10-07 13:31:25
【问题描述】:

我写了一个小程序,但是主析构函数不能正常工作。这是程序的代码:

#include<iostream.h>

class citizen {
private:
    char* name;
    char* nationality;
public:             
    citizen(char* name, char* nationality) {
        this->name = name;
        this->nationality = nationality;
    }                  

    citizen(const citizen &obj) {
        name = obj.name;
        nationality = obj.nationality;
    }        

    void display() {
        cout << "Name: " << name << endl;
        cout << "Nationality: " << nationality << endl;
    }

    ~citizen() { 
        if(name){
            delete[]name;
        }
        if(nationality) {
            delete []nationality;                          
        }        
    }       
};

main() {
   citizen obj1("Ali", "Pakistani");
   obj1.display();
   { 
      citizen obj2 = obj1;                 
   }
   obj1.display();
   system("pause");
}

我所知道的是,在main 函数中,我将obj1 的状态分配给obj2,从那个位置它们现在都指向同一个内存区域。而代码citizen obj2 = obj1; 位于两个大括号之间。

   { 
      citizen obj2 = obj1;                 
   }

所以在执行第二个花括号后,obj2 应该销毁并删除变量namenationality。当我第二次调用obj1.display(); 时,它应该在屏幕上显示垃圾。

obj1 仍在打印我在构造函数中提供的确切名称,即使它不应该是。

请解释这种行为。

【问题讨论】:

  • obj1obj2 保存指向字符串文字的指针。正如我在对您的第一个问题的评论中指出的那样,您不能删除这些。您正在调用未定义的行为。
  • char* 替换为 std::string 您正在将 char* 视为不是指针。
  • 您的deletes 未与对应的news 配对。这应该表明并非一切都很好......此外,在您删除指针后,指针本身不会被切碎或其他东西,因此它仍然指向内存中的相同位置。使用delete[] ptr; ptr = NULL; 来避免这种情况。

标签: c++


【解决方案1】:

您的delete[]s 调用未定义的行为,因为您正试图破坏字符串文字。 任何事情都可能发生。

即使您自己分配了内存,您仍然会遇到未定义的行为,因为您会尝试访问已删除的内存:

obj1.display();
{ 
   citizen obj2 = obj1;                 
}
obj1.display();  // ILLEGAL

因为您没有定义赋值运算符,所以将使用编译器生成的运算符,它只是将指针分配到相同的内存 - 您销毁然后尝试访问的内存。

【讨论】:

    【解决方案2】:

    这段代码有问题。

         if(name){
                  delete[]name;
         }
         if(nationality){                          
                  delete []nationality;                          
         }   
    

    您正在使用new 运算符删除尚未在堆上分配的内容。

    【讨论】:

    • +1。如果您没有分配某些内容,请不要删除它。如果您确实分配了某些内容,则最终必须将其删除。一次。如果你不知道,你最好知道你是否使用了指针。更好的是,不要使用指针。使用诸如 std::string 之类的类型,为您完成所有这些工作。
    【解决方案3】:

    您的复制构造函数只是复制指针(如果您没有提供自己的指针,那么隐式构造函数会这样做),这意味着两个对象都将尝试删除相同的数组。此外,您将指针设置为指向字符串文字,根本不能删除它;您只能删除使用new 创建的对象。简单的解决方案是将内存管理委托给旨在正确执行此操作的类:

    std::string name;
    std::string nationality;
    

    现在您无需为自己的析构函数、复制构造函数或复制赋值运算符操心了;作为奖励,您的课程也可以在 C++11 中正确移动。

    如果您喜欢自己处理内存问题,那么您需要构造函数来分配新的缓冲区并复制内容。请注意异常安全,因为您试图在一个类中处理两个单独的动态资源,这总是会导致错误。您还需要一个复制赋值运算符(根据Rule of Three),为了提高效率,您还可以考虑使用移动构造函数和移动赋值运算符。

    此外,可能值得更新到本世纪的语言版本之一。 &lt;iostream.h&gt; 已经有十五年没有成为标准标题了。

    【讨论】:

      【解决方案4】:

      其他人已经指出了与字符串相关的错误,但我认为你犯了一个更根本的错误:delete does not破坏事物*;它只是释放内存以供重用并调用相关的析构函数。这意味着在delete 操作之后,通常完全可以使用已删除的对象而不会回收垃圾。

      *- 在某些实现中,在__DEBUG 模式下,它会标记已释放的内存,以便您发现这些错误,但这不是标准的一部分。

      【讨论】:

      • “这意味着它通常完全可以在删除之后使用已删除的对象” - 并非没有遇到未定义的行为。
      • @LuchianGrigore:嗯,呵呵。
      • 是的,呃。但是对于提出这个问题的人来说,这并不是那么明显。事实上,这种说法可能完全令人困惑。
      【解决方案5】:

      你应该只在使用new operator的类中使用dynamically allocateddelete内存块

      citizen(char* aname, char* anationality){
      
           size_t nameSize = strlen(aname)
           size_t nationalitySize = strlen(anationality)
      
           this->name = new char[nameSize];
           this->nationality = new char[nationalitySize];
      
           strcpy(this->name, aname, nameSize);
           this->name[nameSize ] = NULL;
      
           strcpy(this->nationality , anationality , nationalitySize);
           this->nationality [nationalitySize] = NULL;
      }
      

      如果你在constructor 中创建了一个内存,那么你在destructor 中删除它们。 因为你有一些任务,那么你应该实现一个copy constructor,它创建一个内存并复制=右侧的数据operator

      最后的想法,如果你不能处理指针来避免memory leaks,你最好使用字符串对象@

      【讨论】:

        【解决方案6】:

        您的第二次显示调用成功了,这纯属幸运。

        正如 Jack Aidley 所指出的,delete 并没有从字面上删除值,只是将内存区域标记为可用。因此,如果没有其他应用程序分配和修改该释放区域,则之前的值可能保留在那里。此外,删除后,您仍然保留指向该地址的指针。

        总之,您访问的是同一个内存区域,其中包含旧值。但这只是运气,因为该区域没有被任何人修改。

        为防止此类错误,您应该始终将 NULL 分配给未使用的指针,以便下次尝试访问它们时遇到 Access Violation 错误。一些编译器(例如 MSVC)使用签名值(例如 0xDDDDDD)重写释放的内存,以便在调试期间可以轻松发现问题。 Check this answer for details

        最后,delete 应该与 new 匹配,否则行为是未定义的,所以这又是运气。我什至无法运行您的应用程序并显示结果。它不断崩溃并给出内存错误。

        【讨论】:

        • thanx bro 我已经替换了这个构造函数的代码citizen(char* name, char* nationality) { this->name = name;这->国籍=国籍; } 使用此代码citizen(char* name, char* nationality){ this->name = new char[strlen(name)+1]; strcpy(this->name, name); this->nationality = new char[strlen(nationality)+1]; strcpy(this->nationality, 国籍); } 最后它工作正常。谢谢
        • 是的,这看起来更好 :) 顺便说一句,您并没有对此进行试验,但不要忘记将 name = NULLnationality = NULL 放入析构函数以防止非法访问。因为访问释放的内存空间被认为是非法的。
        【解决方案7】:

        谢谢大家。我已经替换了这个构造函数代码

        citizen(char* name, char* nationality) {
            this->name = name;
            this->nationality = nationality;
        }        
        

        使用此代码

        citizen(char* name, char* nationality){ 
           this->name = new char[strlen(name)+1]; 
           strcpy(this->name, name); 
           this->nationality = new char[strlen(nationality)+1]; 
            strcpy(this->nationality, nationality); 
        } 
        

        最后它工作正常。谢谢

        【讨论】:

          猜你喜欢
          • 2020-10-24
          • 2014-05-12
          • 2011-05-24
          • 2018-11-24
          • 1970-01-01
          • 2010-11-05
          • 1970-01-01
          • 1970-01-01
          • 2021-07-04
          相关资源
          最近更新 更多