【问题标题】:Destructor, doesn't delete my object析构函数,不会删除我的对象
【发布时间】:2012-04-30 05:47:40
【问题描述】:

我有一个大问题 - 即我的析构函数没有删除对象,在我的代码中,当我调用 l3.~list(); 时,我将在 main 中粘贴它,它只删除单链表(这很好),但它没有删除 char* 名称,即使我在我的析构函数 delete [] name; 中声明。有什么想法吗?

这里是代码;

#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;

class list{

    struct lista
    {
        int num;
        char* word;
        lista* next;
    };
    lista* head;
    char* name;
    public:
    list(char* name1){head=NULL;name=new char[strlen(name1)+1];strcpy(name,name1);}
    char getChar(int key, int index);
    void setChar(int key, int index, char c);
    void insert(int number,char* txt);
    void remove(int number);
    void print();
    list(const list &o);
    list& operator=(const list &x);
    ~list();
};
void list::insert(int number,char* txt){
    lista* ptr,*tmp;
    ptr=head;
    lista* newlista=new lista;
    newlista->num=number;
    newlista->next=NULL;
    newlista->word= new char[strlen(txt)+1];
    strcpy(newlista->word,txt);
    if(head==NULL){
        head=newlista;
        newlista->next=NULL;
    }
    else while(ptr!=NULL){
        if(strcmp(txt,ptr->word)>=0){
            if(ptr->next!=NULL && strcmp(txt,ptr->next->word)<=0)
            {
                tmp=ptr->next;
                ptr->next=newlista;
                newlista->next=tmp;
                break;
            }
            else if(ptr->next!=NULL && strcmp(txt,ptr->next->word)>0)
                ptr=ptr->next;
            else
            {
                //next is empty
                ptr->next=newlista;
                break;
            }
        }
        else{
            //txt mniejszy niz w 1szym elemencie
            newlista->next=head;
            head=newlista;
            break;
        }      
    }
    return;
}

void list::print(){
    cout<<name<<";"<<endl;
    lista *druk;
    druk=head;
    while(druk!=NULL){
        cout<<"txt: "<<druk->word<<" | "<<"num: "<<druk->num<<endl;
        druk=druk->next;
    }
    cout<<endl;
    return;
}


void list::remove(int number){
    if(head==NULL)
        return;
    if(head->num==number){
        lista* ptr=head;
        head=head->next;
        delete [] ptr->word;
        delete ptr;
        return;
    }
    lista* ptr=head;
    while(ptr->next!=NULL && ptr->next->num!=number)
        ptr=ptr->next;
    if(ptr->next==NULL){
        cout<<number<<" element not found"<<endl;
        return;
    }
    lista* todelete=ptr->next;
    ptr->next=todelete->next;
    delete [] todelete->word;
    delete todelete;
    return;
}
list::list(const list &o)
{
    lista *xtr = o.head;
    head=NULL;// bez tego nie działa
    lista *etr=head;// nastawic etr na head?
    while (xtr)
    {
        lista* ntr = new lista;
        if (!ntr)
        {
            cerr << "list::CopyConstructor: Allocation memory failure!";
            cerr << endl;
            break;
        }
        ntr->num = xtr->num;
        ntr->word= new char[strlen(xtr->word)+1];
        strcpy(ntr->word,xtr->word);
        ntr->next = NULL;
        if (head)
            etr->next = ntr;    
        else
            head = ntr;
        etr = ntr; // keep track of the last element in *this
        xtr = xtr->next;
    }
    name = new char[strlen(o.name)+5];
    strcpy(name,o.name);
    strcat(name,"Copy");
}

list& list::operator=(const list &x)
{
    if(this==&x)
        return *this;
    lista *etr=head;
    while(etr) // removing list from this
    {
        etr=etr->next;
        delete head;
        head=etr;
    }
    lista *xtr=x.head;
    while(xtr)
    {
        int copied=xtr->num;
        lista *ntr= new lista;
        ntr->word=new char[strlen(xtr->word)+1];
        if (!ntr) 
        {
            cerr << "list::operator=: Allocation memory failure!" << endl;
            break;
        }
        ntr->num=copied;
        strcpy(ntr->word,xtr->word);
        ntr->next=NULL;
        if (!head)
            head = ntr;
        else
            etr->next = ntr;

        etr = ntr; // keep track of the last element in *this
        xtr = xtr->next;
    }
    char *name=new char[strlen(x.name)+1];
    strcpy(name,x.name);
    return *this;
}

list::~list()
{
    cout<<"Object with name:"<<name<<" destroyed!"<<endl;
    delete [] name;
    lista *dtr=head;
    while(dtr) // removing lista from this
    {
        dtr=dtr->next;
        delete [] head->word;
        delete head;
        head=dtr;
    }

}
void f();
void f(){
    list o("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
    o.insert(4,"kazio");
    o.insert(100,"312jh31io2");
    o.insert(34,"kz31231azio");
    o.insert(1020,"123213312jh31io2");
    o.insert(213123,"z3213io");
    o.insert(1100,"zdrf312jh31io2");
    o.print();
}
int main(){
    list l1("lista1");
    l1.insert(5,"Endian");
    l1.insert(7,"Endianness");
    l1.insert(100,"Hexediting");
    l1.insert(34,".mil");

    l1.print();
    list l2(l1); // usage of CC - the same as list l2=l1; 
    l2.print();
    l2.remove(5);
    l2.print();
    l1.print();

    list l3("asajnment");
    l3=l2=l1;
    l3.print();
    l2.print();
    f();
    l3.print();
    l3.~list(); // here i use destructor on l3
    l3.print(); // l3 is being printed with weird name, even though it should be destroyed
    getchar();
    return 0;
}

【问题讨论】:

  • 为什么要直接调用析构函数?你不应该那样做。
  • 嗯,它不能,因为首先这样做是不合适的。
  • 请阅读一本不错的 C++ 书籍 - 这是一门复杂的语言,您很容易理解一些令人讨厌的误解。
  • 如果您在调用析构函数后设置断点,您可以看到正确的值或垃圾 - 这是未定义的行为。您不应该引用已删除的对象。
  • 另外,检查new返回的指针是否不为空也是不必要的,因为分配失败时会抛出std::bad_alloc类型的异常。 new 有一个 nothrow 版本,但您可能不想使用它。从文件名等我看到这是家庭作业。只是为了我的好奇心 - 上哪所大学?

标签: c++ list linked-list destructor


【解决方案1】:

在调用析构函数后调用任何方法会导致未定义的行为 - 它可能会或可能不会起作用,并且会产生奇怪的结果。

另外,你不应该直接调用析构函数:

  • 当对象在堆栈上分配时,它会在作用域结束时自动销毁。 (范围是大括号{}之间的东西)
  • 当对象在堆上分配时,使用new,它应该使用delete销毁。

【讨论】:

  • 即使什么都不做也会导致未定义的行为!当对象超出范围时,会自动调用析构函数,因此在这种情况下,它会被调用两次。坏消息。
  • 另外,你不能直接在具有静态存储持续时间的对象上调用析构函数(即在函数和类之外声明,或声明static);这些在程序退出时会自动销毁。唯一一次直接调用它是对象是使用位置new 创建的。
【解决方案2】:

C++ 析构函数不像您可能用 C 编写的释放函数。它们更好:在RAII idiom 中,您可以在对象退出作用域的那一刻进行销毁。这意味着您通常根本不必关心释放资源:只需等到不再需要该对象(因为它无法访问),此时它会自动删除(包括调用析构函数,是的,这是可以安全调用它的唯一方法)。如此出色的 C++ 在许多方面都与垃圾收集语言一样好,但没有它们的一些缺点。

获得 RAII 优势的最简单方法是使用标准容器和智能指针。在您的情况下,将lista* next 替换为std::unique_ptr&lt;lista&gt; next 并将char* word 替换为std::string word,并且完全不需要定义析构函数

【讨论】:

    【解决方案3】:

    这段代码有很多错误,我不知道从哪里开始......

    • 使用 std::string
    • 使用 std::map 将 int 值与字符串相关联。这几乎已经可以满足您的需求了。
    • 不要为任何不是新的东西调用析构函数。要删除某些内容,请使用 delete/delete[] 并且不要直接调用析构函数。如果您确实使用 new,请使用 RAII 习惯用法来管理对象,例如 std::unique_ptr 或 std::shared_ptr 以避免手动调用 delete/delete[] 并编写异常安全代码

    这是一个稍微改进的版本。请注意,没有一次调用 new/delete。

    #include <iostream>
    #include <string>
    #include <map>
    #include <cstdio>
    
    class list
    {
    public:
        explicit
        list( std::string n ) : name( n ) {}
    
        ~list() { std::cout << "~list:" << name << std::endl; }
    
        void list::insert(int number, std::string const& txt ){
            items.insert( std::make_pair(number,txt));
        }
    
        void list::remove(int number){
            items.erase( number );
        }
    
        void print( ){
            std::cout << name << ";" << std::endl;
            for( Items::const_iterator it = items.begin(), end = items.end(); it != end; ++it  )
            {
                std::cout << "num: " << it->first << " | " << "txt: " << it->second << std::endl;
            }
            std::cout << std::endl;
        }
    
    private:
        typedef std::map<int,std::string> Items;
        Items items;
        std::string name;
    };
    
    int main()
    {
        list l1( "lista1" );
        l1.insert( 5, "Endian");
        l1.insert( 7, "Endianness");
        l1.insert( 100, "Hexediting");
        l1.insert( 34, ".mil");
        // extra scope so the destructor of l2 is called before call to getchar
        { 
            list l2( l1 ); 
            l2.remove( 5 );
            l2.print();
        }
        l1.print();
    
        getchar();
        return 0;
    }
    

    【讨论】:

      【解决方案4】:

      确保在销毁后不会错误访问您的成员的一种方法是在删除它们后将所有指针设置为 NULL。

      这样,您就可以放心,之后没有人可以访问您的敏感数据,因为您不再指向它。而且您可以再次调用析构函数而不会产生不良副作用,因为允许在 NULL 指针上调用 delete 并且什么都不做。

      【讨论】:

      • 我相信这与他的问题非常相关,但你应该解释一下如何以及为什么
      • 其他答案已经解释最多了;最好确保对象在销毁后不会被访问。所以我不需要再解释了,我只是指出如果你有程序在销毁对象后可能会访问它们的情况,你可以采取安全措施。
      【解决方案5】:

      如果您在删除对象后打印它的内存状态,您将看到该值保持不变,直到您不分配新对象。为您的程序分配的内存只能变得更大。当您删除数据时,它们不会设置为“0”,只是标记为空闲以供下一个分配对象使用。

      编辑:我的意思是如果你在 free 之后创建一个具有未初始化值的新对象,他可以取回存储在内存中的旧值。

      【讨论】:

      • 我认为这不对。分配给程序的内存可能会下降。您似乎对虚拟内存的工作方式也感到困惑。
      • 我经历过,我也在官方文档上读到过:cplusplus.com/reference/clibrary/cstdlib/free也许我不太理解:/实际上你可以得到segfault。
      • 内存被释放后,是UB来访问它,但在实践中:有时它会释放到OS,但通常发生的情况就是你描述的。
      猜你喜欢
      • 2013-09-30
      • 2021-11-05
      • 2021-04-10
      • 2013-01-16
      • 1970-01-01
      • 2013-05-21
      • 2012-02-05
      • 2017-05-18
      • 2011-11-05
      相关资源
      最近更新 更多