【问题标题】:Seg fault after is item pushed onto STL container将项目推送到 STL 容器后的段错误
【发布时间】:2010-10-15 03:55:40
【问题描述】:
typedef struct temp  
{  
        int a,b;  
        char  *c;  
        temp(){ c = (char*)malloc(10);};  
        ~temp(){free(c);};  
}temp;  

int main()  
{  
   temp a;  
   list<temp>   l1;  
   l1.push_back(a);  
   l1.clear();  
   return 0;  

}  

给出分段错误。

【问题讨论】:

  • 除非您有充分的理由不这样做,否则您应该尝试养成使用 new & delete 而不是 malloc & free 的习惯。
  • 你不需要在 C++ 中“typedef”结构,只需要在 C 中。
  • 重要提示:当你定义任何类或结构时,尤其是指针成员,声明 operator= 和复制构造函数为私有的。 “私有:temp& operator=(const temp&); temp(const temp&);”如果你对需要复制的类做任何事情,它不会编译,你知道你必须提供它们。
  • ... 要么写一个做正确的事,在这种情况下,或者只是删除声明并获取默认副本,如果这些对有问题的类有用。跨度>

标签: c++ memory memory-management stl pointers


【解决方案1】:

你没有复制构造函数。

当您将“a”推入列表时,它会被复制。 因为您没有复制构造函数(为 c 分配内存并从旧 c 复制到新 c) c 是 a 中的相同指针和列表中 a 的副本。

两个a的析构函数都被调用,第一个会成功,第二个会失败,因为c指向的内存已经被释放了。

你需要一个拷贝构造函数。

要查看发生了什么,请在构造函数和析构函数中添加一些 cout,然后逐步执行代码。

【讨论】:

  • 投过票的人可以告诉我为什么,如果我的回答有问题,我想知道它是什么。谢谢大家
  • 我没有 -1 你,但 c 没有被删除 - 删除对象和释放内存不是一回事。
  • @Pete:你当然是对的,我的手指误会了我,谢谢 :)
  • 在复制 ctor 中使用交换习语来避免额外的内存分配会很好。
【解决方案2】:

你需要一个深拷贝构造函数来避免双重释放()。您有一个 temp class (a) 的变量,然后将其添加到列表中。变量被复制。然后你清除列表,里面的元素被销毁并调用 free()。然后 a 变量被销毁,并且 free() 再次调用相同的地址导致分段错误。

您需要一个复制构造函数来深度复制类临时变量,这将 malloc() 另一个缓冲区并复制数据。

【讨论】:

    【解决方案3】:

    当您调用l1.push_back(a) 时,“a”的第二个副本是复制构造的。因此,现在有两个类认为它们拥有原始 malloc 调用的内存,当第二个类被删除时,它会尝试释放第一个类删除的内存。

    解决方案是添加一个复制构造函数来处理类的实例实际上并不拥有数据这一事实。通常,您会通过某种形式的引用计数来做到这一点。

    【讨论】:

      【解决方案4】:

      除了给出的修复之外,您应该避免在 C++ 中使用 malloc/free。在您的特定情况下,我会使用矢量:

      #include <vector>
      
       typedef struct temp  
      {  
              int a,b;  
              std::vector<char> c;  
              temp(){ c.reserve(10);};  
      }temp;  
      
      int main()  
      {  
         temp a;  
         list<temp>   l1;  
         l1.push_back(a);  
         l1.clear();  
         return 0;  
      
      }
      

      【讨论】:

      • 这是一个不幸的例子。不太了解 STL 的人可能会认为 reserve() 会改变向量的大小(实际上是添加元素),而实际上它只分配一个缓冲区。
      • 我知道是的;我这样做是因为我认为他想使用 push_back 命令。
      【解决方案5】:

      如果您不想添加复制构造函数,可以考虑使用指向值的指针列表而不是值列表。

      list<temp*>   l1;
      l1.push_back( new temp() );  
      

      但是你必须手动删除列表中的每个对象以防止内存泄漏。

      结构中的 a,b 成员也未初始化。小心点。

      【讨论】:

        【解决方案6】:

        在这种情况下,除了复制构造函数之外,最好也提供一个 = 运算符。

        struct temp {   // typedef is implicit in C++
          int a,b;
          char * c;
        
          // Constructor
          temp() { c = malloc(10); }
          // Destructor
          ~temp() { free(c); }
          // Copy constructor
          temp(const temp & x) { c = malloc(10); setTo(x); }
          // Operator =
          temp & operator = (const temp & x) { setTo(x); return *this; }
        
          // Initialize THIS to X
          void setTo(const temp & x) { a = x.a; b = x.b; memcpy(c,x.c,10); }
        };
        

        【讨论】:

          【解决方案7】:

          您需要一个复制构造函数和一个赋值运算符 - 存储在 STL 集合中的内容作为副本存储,并且可以在向量更改大小时重新分配。

          很难从您的代码中确切地看出复制构造函数的语义应该是什么,但至少分配一些内存以便(如果没有别的)析构函数有一些东西可以释放。如果没有更多类的详细信息,赋值运算符同样难以指定。

          【讨论】:

            【解决方案8】:

            您需要为temp 定义一个复制构造函数。现在,当您将a 推入列表时会发生a 的副本。副本(称为a2)通过a2.c = a.c 进行初始化。这意味着aa2 都指向同一个内存块。当它们的析构函数被调用时,该内存块被释放两次,导致分段错误。

            鉴于您发布的内容,复制构造函数应该是这样的:

            temp::temp (temp const &rhs)
            {
                this->a = rhs.a;
                this->b = rhs.b;
                this->c = (char *) malloc (10);
                memcpy (this->c, rhs.c, 10);
            }
            

            这假设 c 指向的总是 10 个字符长...

            【讨论】:

              【解决方案9】:

              这段代码的另一个问题是额外的分号

              temp(){ c = (char*)malloc(10);};
              ~temp(){free(c);};
              

              最好删除它们:

              temp(){ c = (char*)malloc(10);}
              ~temp(){free(c);}
              

              【讨论】:

                【解决方案10】:

                具有“STL”标签但缺少 STL 是导致问题的原因,这真是太讽刺了。

                【讨论】:

                • 那会是什么“缺乏 STL”?
                • 使用 STL 可以避免调用 malloc()/free()。您也不需要任何析构函数或复制构造函数,因此可以讨论大多数发布的解决方案。
                猜你喜欢
                • 2016-02-03
                • 2020-10-15
                • 2017-03-12
                • 2013-01-07
                • 1970-01-01
                • 1970-01-01
                • 2019-04-16
                • 1970-01-01
                • 2018-05-11
                相关资源
                最近更新 更多