Handwritten linked list is segfaulting and I don&#

2019-07-21 05:35发布

问题:

Hi I was working on a bit of fun, making an interface to run gnuplot from within c++, and for some reason the my linked list implementation fails.

The code below fails on the line plots->append(&plot). Stepping through the code I discovered that for some reason the destructor ~John() is called immediately after the constructor John(), and I cannot seem to figure out why.

The code included below is a stripped down version operating only on Plot*. Originally I made the linked list as a template class. And it worked fine as ll<int> and ll<char*> but for some reason it fails as ll<Plot*>.

Could youp please help me figure out why it fails? and perhaps help me understand how to make it work?

In advance: Thanks a heap!

//B2S

#include <string.h>

class Plot{
  char title[80];
public:
  Plot(){  }
};

class Link{
  Plot* element;
  Link* next;
  Link* prev;
  friend class ll;
};

class ll{
  Link* head;
  Link* tail;
public:
  ll(){
    head = tail = new Link();
    head->prev = tail->prev = head->next = tail->next = head;
  }
  ~ll(){
    while (head!=tail){
      tail = tail->prev;
      delete tail->next;
    }
    delete head;
  }
  void append(Plot* element){
    tail->element = element;
    tail->next = new Link();
    tail->next->prev = tail;
    tail->next = tail;
  }
};

class John{
  ll* plots;
public:
  John(){
   plots= new ll();
  }
  ~John(){
    delete plots;
  }
  John(Plot* plot){
    John();
    plots->append(plot);
  }
};   

int main(){
  Plot p;
  John k(&p);
}

回答1:

The statement:

 John();

Just constructs a new nameless John which is created and immediately destroyed. There is no way (in the current version of C++) to call a constructor from another constructor or on an already constructed object.

This means that the plots member of your John called k is never being initalized, hence the crash when you call append from the John constructor.

[As Neil Butterworth comments, if you had actually constructed an ll instance there are other issues with it, this is just the most immediate problem.]



回答2:

Because you can't call alternate constructors like that in C++. In...

John(Plot* plot){
  John();
  plots->append(plot);
}

Your first line constructs a new John object using the default constructor, then throws the result away. It then calls append on plots (which as it has not been set to anything, is junk). Refactor the real plots initialisation code into a private method, then call this init method in both constructors before doing anything else.



回答3:

head = tail = new Link();

Here you assign head and tail to point to the same link object. Since you never reassign either of them, that means that any time you change any property of head (e.g. head.next = foo), the same change is applied to tail.

Meaning head and tail will always have the same element, the predecessor and the same successor. Obviously this does not lead to the intended results.



回答4:

  Plot p;
  John k(&p);

Use this instead:

  Plot * p = (Plot*) new Plot;
  John k(p);

This code is broken, since once p goes out of scope, k's reference to it will die. This is not the bug that caused the segfault, but it will probably cause a segfault later on once you fix your other bugs.

You will need to delete p later.