析构函数不会破坏对象

时间:2013-10-07 13:31:25

标签: c++

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

#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仍在打印我在构造函数中提供的确切名称,即使它不应该。

请解释一下这种行为。

7 个答案:

答案 0 :(得分:2)

您的delete[]调用未定义的行为,因为您试图销毁字符串文字。 任何都可能发生。

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

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

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

答案 1 :(得分:1)

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

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

现在您不需要为自己的析构函数,复制构造函数或复制赋值运算符而烦恼;作为奖励,你的课程也可以在C ++ 11中正确移动。

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

此外,可能值得更新本世纪版本的语言之一。 <iostream.h>大约十五年来一直不是标准标题。

答案 2 :(得分:1)

此代码有问题。

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

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

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

您应该只使用delete

dynamically allocated在课堂上new operator内存块
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

,最好使用字符串对象

答案 5 :(得分:0)

你的第二次显示通话工作真的很幸运。

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

总之,您正在访问具有旧值的相同内存区域。但这只是运气,因为该地区没有被任何人修改过。

为防止出现这类错误,您应始终为未使用的指针指定NULL,以便下次在尝试访问时出现访问冲突错误。某些编译器(如MSVC)使用签名值(例如0xDDDDDD)重写释放的内存,以便在调试期间可以轻松捕获问题。 Check this answer for details

最后,delete应与new匹配,否则行为未定义,所以它再次只是运气。我甚至无法运行您的应用程序并显示结果。它不断崩溃并导致内存错误。

答案 6 :(得分:0)

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

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); 
} 

最后它工作正常。谢谢