【问题标题】:C++ simple linked list attempt : don't quite understand what's going onC++ 简单链表尝试:不太明白发生了什么
【发布时间】:2014-02-02 07:51:01
【问题描述】:

我尝试在 C++ 中严格实现我目前正在研究的算法,即带有简单链表的递归函数。这是我的来历:

#include <iostream>
using namespace std;

class Liste {
    private :
        int val;
        Liste *suivante;
    public :
        Liste(int val = 0, Liste *suivante = NULL) {
            this->val = val;
            this->suivante = suivante;
        }
        void afficherElement() const {
            cout << "Adresse : " << this << endl;
            cout << "Valeur : " << val << endl;
            cout << "Adresse suivante : " << suivante << endl;
            cout << endl;
        }
        int tete() const {
            return val;
        }
        Liste reste() const {
            return *suivante;
        }
        bool estVide() const {
            return (suivante == NULL);
        }
        Liste prefixer(int val) {
            Liste *nouvelle = new Liste(val, this);
            return *nouvelle;
        }
        Liste suffixer(int val) {
            suivante = new Liste(val);
            afficherElement(); // test (last element won't be displayed)
            return *suivante;
        }
};

int main() {
    Liste uneListe(3); // 1st element
    uneListe.suffixer(5).suffixer(8).suffixer(10); // adding 3 more

    cout << "-----------\n\n";

    uneListe.afficherElement(); // displaying 1st element : ok
    uneListe.reste().afficherElement(); // displaying 2nd element : pointer is NULL !!???
    // uneListe.reste().reste().afficherElement(); --> segmentation fault, predictably enough

    return 0;
}

如您所料,它不起作用。当我添加元素时,在 add 方法中调用 display 方法时,元素似乎格式正确,尽管指针值和下一个元素的地址不同(我不明白为什么)。 但是,添加过程完成后,我尝试再次显示列表,第一个元素与第二个元素很好地链接,但是有一个 NULL 指针值。想知道为什么 ?? 我看过一个包含两个类(NodeList)的代码,效果很好,但我想知道我的有什么问题。是我在同一个类中创建一个类的新对象吗?

谢谢,

【问题讨论】:

  • 我能看到的最大问题是后缀/前缀是“列表”的返回副本,而不是引用。所以最重要的是 .suffixer(8) 被应用于 List 对象的副本,而不是您在调用 .suffixer(5) 时创建的那个。
  • 您可以尝试通过引用返回后缀和前缀吗?即 Liste& 后缀(int val)。我认为问题在于您在第二个之后添加的下一个元素将被添加到不同的列表中,因为您按值返回。如果你调试,你可能会看到这个。
  • 另外,如果我可以提出建议的话。我实际上会进行设计更改。有一个班级Liste 和一个班级Noeud。基本上,列表有一个指向第一个节点的指针,并负责将节点链接在一起。而不是 list 是当前的节点。
  • 你能用nullptr代替NULL吗?它更像是 c++-ey
  • 看来你很准。返回值是一个错误,它解释了指针值和对象地址的差异。谢谢。将发布包含所有必要更正的代码。

标签: c++ list linked-list


【解决方案1】:

对于这个问题,你最改变这一行
列表后缀(int val)

Liste* 后缀(int val)

然后改变这一行
返回 *suivante;

追随;
主要使用这条线
uneListe.suffixer(5)->suffixer(8)->suffixer(10);
而不是
uneListe.suffixer(5).suffixer(8).suffixer(10);

【讨论】:

    【解决方案2】:

    您的类方法Liste::prefixer(int val)Liste suffixer(int val) 将返回所创建对象的副本,它们应该返回指向该对象的指针(或引用)。

    例如

    Liste *Liste::suffixer(int val){
        if(suivante == nullptr)
            suivante = new Liste(val);
        else
            throw std::runtime_error("Generic error message");
    
        return suivante;
    }
    

    Liste &Liste::suffixer(int val){
    
        ... previous inner method ...
    
        return *suivante;
    }
    

    【讨论】:

      【解决方案3】:

      类 Liste 包含一个值和一个引用,这 不是 list 是什么:单链表是一个 指针 元素包含一个值和一个指向下一个节点的指针。

      您可以使用 value+pointer 元素作为列表对象,忽略 val 成员。这需要对某些方法进行不同的编码,例如 tete() 和 reste()。

      但是,由于使用了

      typedef Liste * real_list_type;
      

      是你的想法(? - 见下文),让我们看看方法。

      bool estVide() const { return (suivante == NULL); }
      

      这与 real_list_type 只是一个 List * 相矛盾;如果你把它和reste() 方法比较,它实际上测试的是tail 是否为空,而不是列表本身! (这将与使用值+指针对象作为列表对象同步。)

      Liste suffixer(int val) { suivante = new Liste(val); ... }
      

      这很糟糕:它将 suivante 替换为一个新对象,无论其中存储了什么(内存泄漏)。你必须这样做

      Liste suffixer(int val) {
        if( suivante == NULL ){
           suivante = new Liste(val);
        } else {
           suivante->suffixer( val );
        }
        return *this;
      }
      

      稍后

      我认为这是使其尽可能接近抽象概念的最佳方式。请注意,没有“isEmpty”——这是通过测试表示列表的 List * 变量是否等于 NULL 来完成的,但你不能有这样的方法。

      template<typename T>
      class List {
      public:
        List( T v, List* t = nullptr ) : value(v), next(t){}
        ~List(){ delete next; }
        List* prepend( T v ){
          return new List( v, this );
        }
        List* append( T v ){
          if( next == nullptr ){
            next = new List( v );
          } else {
            next->append( v );
          }      
          return this;
        }
        T head(){ return value; }
        List* tail(){ return next; }
        void dump(){
          List* curr = this;
          std::string del = "";
          while( curr != nullptr ){
            std::cout << del << curr->value;
            del = ", ";
            curr = curr->next;
          }
         std::cout << std::endl;
        }
      
      private:
        T value;
        List* next;
      };
      
      int main(){
        typedef List<int> * intList;
        intList list = new List<int>( 1 );
        list->append( 2 )->append( 3 );
        list->dump();
      }
      

      【讨论】:

      • 感谢您的解释。我知道我的列表类不是一个正确的列表,我试图坚持我们在算法课程中练习的相当抽象的“递归列表”概念(“包含”列表的列表,“包含”列表等)。 suffixer() 确实存在明显的内存问题。
      • @user3262284 好吧,我试图涵盖所有角度。但是estVide不符合你的概念。
      • 嗯,谢谢。得研究你的代码,我以前从未使用过模板。
      【解决方案4】:

      这是我第一次尝试的“固定”版本:

      #include <iostream>
      using namespace std;
      
      class Liste {
      private :
          int val;
          bool vide;
          Liste *suivante;
      public :
          Liste(int val = 0, bool vide = true, Liste *suivante = NULL) {
              this->val = val;
              this->vide = vide;
              this->suivante = suivante;
          }
          void afficherElement() const {
              cout << "Adresse : " << this << endl;
              cout << "Valeur : " << val << endl;
              cout << "Vide : " << vide << endl;
              cout << "Adresse suivante : " << suivante << endl;
              cout << endl;
          }
          int head() const {
              return val;
          }
          Liste *reste() const {
              return suivante;
          }
          bool estVide() const {
              return vide;
          }
          Liste *prefixer(int val) {
              Liste *nouvelle = new Liste(val, this);
              return nouvelle;
          }
          Liste *suffixer(int val) {
              if(suivante == NULL) {
                  suivante = new Liste(val);
                  vide = false;
              }
              return suivante;
          }
      };
      
      void afficherListe(Liste *uneListe) {
          (*uneListe).afficherElement();
          if(!(*uneListe).estVide()) {
              afficherListe((*uneListe).reste());
          }
      }
      
      int main() {
          Liste *test = new Liste(3);
          (*test).suffixer(5);
          afficherListe(test);
          return 0;
      }
      

      正如预期的那样,这非常不切实际。 劳恩的解决方案看起来不错...... 然而,整个事情很奇怪,我想我最好还是坚持使用常规的 List/Nodes 方式。肯定会和我的老师谈谈。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-10-18
        • 2014-12-06
        • 1970-01-01
        • 2021-10-24
        • 2018-01-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多