0

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

#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我分配 to 状态的函数中obj1obj2从那个地方他们两个现在都指向同一个内存区域。而代码citizen obj2 = obj1;位于两个大括号之间。

   { 
      citizen obj2 = obj1;                 
   }

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

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

请解释这种行为。

4

7 回答 7

3

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

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

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

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

于 2013-10-07T13:33:50.820 回答
2

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

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

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

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

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

于 2013-10-07T13:37:28.483 回答
2

这段代码有问题。

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

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

于 2013-10-07T13:38:14.870 回答
0

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

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

于 2013-10-07T13:37:09.670 回答
0

您的第二个显示调用有效,这纯属幸运。

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

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

为防止此类错误,您应该始终将 NULL 分配给未使用的指针,以便下次尝试访问它们时遇到访问冲突错误。一些编译器(例如 MSVC)使用签名值(例如 0xDDDDDD)重写释放的内存,以便在调试期间可以轻松发现问题。检查此答案以获取详细信息

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

于 2013-10-07T14:30:39.717 回答
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); 
} 

最后它工作正常。谢谢

于 2015-03-29T16:29:16.393 回答
0

你应该只使用类中delete的内存块dynamically allocatednew 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

于 2013-10-07T13:49:05.490 回答