【问题标题】:segmentation fault in overloading operator =重载运算符中的分段错误 =
【发布时间】:2010-01-03 22:16:08
【问题描述】:

我刚刚在重载类 FeatureRandomCounts 的赋值运算符时遇到了一个段错误,该类具有 _rects 作为其指针成员,指向 FeatureCount 和大小为 rhs._dim 的数组,并且其其他日期成员是非指针:

FeatureRandomCounts &  FeatureRandomCounts::operator=(const FeatureRandomCounts &rhs)  
{  
  if (_rects) delete [] _rects;  

  *this = rhs;  // segment fault

  _rects = new FeatureCount [rhs._dim];  
  for (int i = 0; i < rhs._dim; i++)  
  {  
    _rects[i]=rhs._rects[i];  
  }  

  return *this;    
}

有人知道吗?谢谢和问候!

【问题讨论】:

    标签: c++ segmentation-fault overloading


    【解决方案1】:
    *this = rhs;
    

    调用 operator=(),这是您正在编写的函数。提示无限递归、堆栈溢出、崩溃。

    此外,如果您使用 std::vector 而不是 C 样式的数组,您可能根本不需要实现 operator=()。

    【讨论】:

    • 谢谢,尼尔!在我的代码中,C 数组优于 std::vector。但这仍然是一个可能的解决方案。
    • 如果你更喜欢 C 数组而不是向量,那你就错了,并且为自己做了大量不必要的工作。
    • 您无法正确编写赋值运算符这一事实意味着您可能不知道与使用动态分配的数组相关的一大堆其他陷阱。这意味着您绝对应该使用 std::vector。偏爱 C 数组不是原因,而是对实际情况缺乏了解。
    • Neil:除非你有分配要求,不能通过 std::vector 要求的 std::allocator 接口表达。此外,除了内存之外,还有更多可以获取和释放的资源,并且指针通常被用作该习语的一个简单示例(尽管我认为您对这个问题是正确的)。
    【解决方案2】:

    如前所述,你有无限递归;但是,除此之外,这是实现 op= 的一种万无一失的方法:

    struct T {
      T(T const& other);
      T& operator=(T copy) {
        swap(*this, copy);
        return *this;
      }
      friend void swap(T& a, T& b);
    };
    

    编写正确的复制ctor和swap,异常安全和所有边缘情况都为您处理!

    copy 参数是按值传递 然后改变的。当前实例必须销毁的任何资源都会在 copy 被销毁时处理。这遵循current recommendations 并干净地处理self-assignment


    #include <algorithm>
    #include <iostream>
    
    struct ConcreteExample {
      int* p;
      std::string s;
    
      ConcreteExample(int n, char const* s) : p(new int(n)), s(s) {}
      ConcreteExample(ConcreteExample const& other)
      : p(new int(*other.p)), s(other.s) {}
      ~ConcreteExample() { delete p; }
    
      ConcreteExample& operator=(ConcreteExample copy) {
        swap(*this, copy);
        return *this;
      }
    
      friend void swap(ConcreteExample& a, ConcreteExample& b) {
        using std::swap;
        //using boost::swap; // if available
        swap(a.p, b.p); // uses ADL (when p has a different type), the whole reason
        swap(a.s, b.s); // this 'method' is not really a member (so it can be used
                        // the same way)
      }
    };
    
    int main() {
      ConcreteExample a (3, "a"), b (5, "b");
      std::cout << a.s << *a.p << ' ' << b.s << *b.p << '\n';
      a = b;
      std::cout << a.s << *a.p << ' ' << b.s << *b.p << '\n';
      return 0;
    }
    

    请注意,它适用于手动管理的成员 (p) 或 RAII/SBRM 样式的成员 (s)。

    【讨论】:

    • 不太明白。结构是模板吗?所以通过交换,副本将是原始的 *this?
    • 谢谢!交换是否也会改变“复制”?这不仅仅是作业吗?
    【解决方案3】:
     *this = rhs;  // segment fault
    

    这绝对是不是的方法。您递归地调用=,而不是调用内置的赋值运算符。一个一个地分配变量。不要偷懒。

    【讨论】:

    • 谢谢。我很懒。那么是否可以调用默认的赋值运算符呢?
    • 不,一旦你声明了自己的,编译器就不会生成一个(也不能,因为它会有相同的签名)。
    【解决方案4】:

    下面一行:

      *this = rhs;  // segment fault
    

    将递归调用您的 operator=() 函数,导致堆栈溢出。

    您可能应该将其替换为各个成员字段的直接分配。

    As Neil said,使用std::vector&lt;&gt; 之类的东西会从你的代码中移除大部分责任。如果出于某种原因您不能或不想使用std::vector&lt;&gt;,您可能还需要考虑为您的赋值运算符使用“交换习语”。这将使函数异常安全(如果为FeatureCount 数组分配内存失败并引发异常,则分配给的原始对象将保持不变)。类似于以下内容:

    void FeatureRandomCounts::swap( FeatureRandomCounts& other)
    {
        FeatureCount* tmp_rects = other._rects;
        int tmp_dim             = other._dim;    // or whatever type _dim is
    
        // similarly for other members of FeatureRandomCounts...
    
        // now copy the other contents to 
        this->_rects = other._rects;
        this->_dim   = other._dim;
    
        // assign other members of rhs to lhs
    
        other._rects = tmp_rects;
        other._dim   = tmp_dim;
    
        // etc.
    
        return;
    }
    

    现在您的作业可能如下所示:

    FeatureRandomCounts &  FeatureRandomCounts::operator=(const FeatureRandomCounts &rhs)  
    {  
        FeatureRandomCounts tmp( rhs);  // make a copy
    
        tmp.swap( *this);               // swap the contents of the copy and *this
    
        return *this;
                                        // the contents of tmp (which has the old 
                                        //  stuff that was in *this) gets destructed
    }
    

    请注意,您需要一个正确的复制构造函数才能使其工作,但鉴于 Big 3 rule 您已经需要一个正确的复制 ctor。

    【讨论】:

    • 在你的 swap() 实现中,你至少可以使用 std::swap() 来交换实际的成员。
    • @Martin - 你是对的,当然。我避开了std::anything,因为OP 在某处指出他不能/不会使用std::vector。我应该说清楚,稍后会更新答案以表明应该使用std::swap(实际上,它应该是一个无范围的swap(),并带有适当的using 声明,因此选择最合适的swap()向上,std::swap 是最后的匹配)。
    猜你喜欢
    • 2013-11-21
    • 1970-01-01
    • 1970-01-01
    • 2014-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-14
    相关资源
    最近更新 更多