C++ 析构函数不销毁对象
我写了一个小程序,但主析构函数不能正常工作。以下是程序代码:C++ 析构函数不销毁对象,c++,C++,我写了一个小程序,但主析构函数不能正常工作。以下是程序代码: #include<iostream.h> class citizen { private: char* name; char* nationality; public: citizen(char* name, char* nationality) { this->name = name; this->nationality =
#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");
}
因此,在执行第二个大括号后,obj2
应该销毁并删除变量name
和国籍。当我调用obj1.display()时代码>第二次应该在屏幕上显示垃圾
但obj1
仍在打印我在构造函数中提供的确切名称,尽管它不应该是
请解释此行为。您的delete[]
s调用未定义的行为,因为您试图销毁字符串文字任何事情都可能发生
即使您自己分配了内存,您仍然会遇到未定义的行为,因为您将试图访问已删除的内存:
obj1.display();
{
citizen obj2 = obj1;
}
obj1.display(); // ILLEGAL
因为您没有定义赋值运算符,所以将使用编译器生成的赋值运算符,它只是将指针分配给同一个内存,即您销毁并尝试访问的内存。其他人指出了与字符串相关的错误,但我认为您犯了一个更为根本的错误:删除
不会销毁东西*;它只是释放内存以供重用,并调用相关的析构函数。这意味着在delete
操作之后,通常完全可以使用已删除的对象,而不必回收垃圾
*-在某些实现中,在\u DEBUG
模式下,它会在释放的内存上盖章,以允许您发现这些错误,但这不是标准的一部分。您的复制构造函数只是复制指针(如果您没有提供自己的指针,隐式的指针会这样做),这意味着两个对象将尝试删除相同的数组。此外,您正在将指针设置为指向字符串文本,这是绝对不能删除的;只能删除使用new
创建的对象。简单的解决方案是将内存管理委托给一个设计用于正确执行此操作的类:
std::string name;
std::string nationality;
现在,您不需要为自己的析构函数、复制构造函数或复制赋值操作符而烦恼;作为奖励,您的类也可以在C++11中正确移动
如果你喜欢自己处理内存问题,那么你需要你的构造函数来分配新的缓冲区并跨内存复制内容。注意异常安全,因为您试图在一个类中处理两个独立的动态资源,这总是会导致错误。您还需要一个复制赋值运算符(每一个),为了效率,您也可以考虑一个移动构造函数和移动赋值操作符。
此外,它可能值得更新到本世纪的语言版本之一<代码>
已经有15年没有成为标准标题了。此代码有缺陷
if(name){
delete[]name;
}
if(nationality){
delete []nationality;
}
您正在使用new
操作符删除未在堆上分配的内容。您应该只删除使用new操作符在类中动态分配的内存块
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;
}
如果在构造函数
中创建内存,则在析构函数
中删除它们。
因为您有一些赋值,那么您应该实现一个复制构造函数
,它在=
操作符的右侧创建一个内存并复制数据
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;
}
最后,如果您不能处理指针以避免内存泄漏,那么最好使用string对象
第二次显示调用能够正常工作,这纯粹是运气
正如jackaidley指出的,delete并没有真正地删除值,只是将内存区域标记为可用。因此,如果没有其他应用程序分配和修改该释放区域,则先前的值可能会留在那里。此外,在删除之后,仍然保留指向该地址的指针
总之,您访问的是同一内存区域,其中包含旧值。但这只是运气,因为那个地区没有被任何人改变
为了防止这些类型的错误,您应该始终为未使用的指针分配NULL,以便下次尝试访问它们时,您得到访问冲突错误。某些编译器(如MSVC)使用签名值(如0xDDDD)重写释放的内存,以便在调试过程中轻松发现问题
最后,delete
应该与new
匹配,否则行为是未定义的,所以这也是运气。我甚至不能运行你的应用程序并显示结果。它不断崩溃并出现内存错误。谢谢大家。我已经替换了这个构造函数的代码
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);
}
最后,它运行良好。感谢obj1
和obj2
保留指向字符串文本的指针。正如我在对第一个问题的评论中指出的那样,你不能删除这些。您正在调用未定义的行为。将char*
替换为std::string
您将char*视为不是指针。您的delete
s未与相应的new
s配对。这应该是一个迹象,不是所有的都是好的。。。另外,在删除指针后,指针本身并没有被分解,所以它仍然指向内存中的同一位置。使用delete[]ptr;ptr=NULL“这意味着在删除后使用已删除的对象通常是完全可能的”-不会出现未定义的行为。@LuchianGrigore:嗯,是的,是的。但对于提出这个问题的人来说,这并不是那么明显。事实上,这种说法可能会让人完全困惑。如果您没有分配某些内容,请不要删除它。如果您确实分配了某些内容,那么最终必须将其删除。一旦如果你不知道,你最好知道你是否在使用指针。更好的是,不要使用指针。使用执行所有操作的类型,例如std::string