我写了一个小程序,但主析构函数不能正常工作。这是该程序的代码:
#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
应该销毁并删除变量name
和nationality
。当我第二次打电话给obj1.display();
时,它应该在屏幕上显示垃圾。
但obj1
仍在打印我在构造函数中提供的确切名称,即使它不应该。
请解释一下这种行为。
答案 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);
}
最后它工作正常。谢谢