C ++:复制构造函数崩溃

时间:2016-11-25 20:12:38

标签: c++ c++11

我在编写C ++ HashTable的Copy构造函数时遇到问题。现在下面是类结构

template <class TYPE>
class HashTable : public Table<TYPE>
{
    struct Record
    {
        TYPE data_;
        string key_;
        Record* Next;

        Record(const string& key, const TYPE& data)
        {
            key_ = key;
            data_ = data;
            Next = nullptr;
        }

        Record(const Record& a)  {
            if(!a.key_.empty()){

                if(a.Next == nullptr){
                    Next = nullptr;
                }
                else
                {
                    Record* temp = a.Next ;
                    Record *temp2 = Next;
                    while(temp != nullptr)
                    {
                        temp2 = temp ;
                        temp = temp->Next ;
                    }
                    temp2->Next = nullptr;
                }

                data_ = a.data_ ;
                key_ = a.data_ ;


            } // user-

        };

        int TableSize;
        Record** records;
    }
};

以下是复制构造函数 模板

HashTable<TYPE>::HashTable(const HashTable<TYPE>& other)
{
    records = new Record*[other.TableSize];
    TableSize = other.TableSize;
    for(int i = 0 ; i < other.TableSize; i++)
        records[i]= (new Record(*other.records[i]));    
}

我还在ideone http://ideone.com/PocMTD上发布了代码。复制构造函数的代码似乎崩溃了。我没有看到任何会导致程序崩溃的内存泄漏。我尝试过memcopy,使用insert函数,似乎都失败了。

2 个答案:

答案 0 :(得分:2)

int TableSize;Record** records;替换为std::vector<std::unique_ptr<Record>>

Record中,将Record* Next;更改为Record* Next=nullptr;

停止致电new

包括HashTable(HashTable&&)=default;

HashTable<TYPE>::HashTable(const HashTable<TYPE>& other)
{
  records.reserve( other.records.size() );
  for (auto const& rec_in : other.records)
    records.emplace_back( new Record(*rec_in) ); // make_shared<Record> in C++14
}

现在我们不再进行手动内存管理了。所以一整套担忧都消失了。

接下来,查看原始Next指针。这是坏消息。复制Record时,Next指针指向Record结构集。

我们可以通过几种方式解决这个问题。最简单的方法是使用偏移指针。

template<class T>
struct offset_ptr {
  std::ptrdiff_t offset = std::numeric_limits<std::ptrdiff_t>::max();

  explicit operator bool()const {
    return offset!=std::numeric_limits<std::ptrdiff_t>::max();
  }

  T* get() const {
    return (T*)( offset+(char*)this );
  }
  T* operator->() const { return get(); }
  T& operator*() const { return *get(); }
  operator T*() const { return get(); }

  offset_ptr(std::nullptr_t):offset_ptr() {}

  explicit offset_ptr(T* p) {
    if (!p) return;
    offset = (char*)p-(char*)this;
    Assert(*this);
  }
  offset_ptr()=default;
  offset_ptr(offset_ptr const&)=default;
  offset_ptr& operator=(offset_ptr const&)=default;
  offset_ptr(offset_ptr&&)=default;
  offset_ptr& operator=(offset_ptr&&)=default;
};

而不是按绝对位置存储指针,而是存储偏移量。

现在我们这样做:

template<class TYPE> struct Table{};

template <class TYPE>
class HashTable :public Table<TYPE>
{
public:
  struct Record
  {
    TYPE data_;
    std::string key_;
    offset_ptr<Record> Next;

    Record(const std::string& key, const TYPE& data)
    {
      key_ = key;
      data_ = data;
      Next = nullptr;
    }

    Record(const Record& a) 
    {
      if(!a.key_.empty())
      { 
        if(a.Next == nullptr)
        {
          Next = nullptr;
        }
        else
        {
          auto temp = a.Next;
          while(temp != nullptr)
          {
            Next = temp;
            temp = temp->Next;
          }
        }

        data_ = a.data_;
        key_ = a.data_;
      } 
    }

  };
  std::vector<Record> records;
};

并且不需要复制ctor;偏移量ptr将另一个记录的位置知道为记录中的偏移量。数据按值而不是按引用存储。

请注意,我们有vectorRecord,而不是指向Record的指针。这是offset_ptr工作的关键。调整大小不是问题,因为偏移量保持不变。复制仍然是安全的,因为每一侧的偏移现在都指向其矢量中的其他元素。在中间插入/移除是危险的,但简单地归零元素不是。

请注意,上述std::ptrdiff_t不支持大小为offset_ptr或更高的缓冲区。在一个大约2演出的64位系统上;在64位系统上它很大。 (我不使用0作为空值,因为如果我这样做,offset_ptr<X>作为struct X的第一个成员,如果我指向其封闭的X将会无效。)

boost也有一个较少的定制offset_ptr类型。上面的实现是一个简单的草图,而不是一个可靠的实现。

答案 1 :(得分:0)

你没有在这里显示完整的代码(也不是在ideone上),但让我根据我所看到的情况进行猜测。

  • 我认为您在副本c'tor中传递的other对象具有完整设置的Record s列表。
  • 我进一步假设您的HashTable类有一个析构函数(未显示),它删除了所有链接的Record
  • 您的复制构造函数调用Record的副本c'tor(对于指向Record的指针数组中的每个条目)。 Record coyp c'tor仅生成浅拷贝,即仅复制指向下一个元素的指针(它仍将指向other哈希表中复制的记录的下一个元素。 / LI>

因此,当other及其副本被删除时(在范围或程序的末尾;未显示),您将有双重删除(崩溃)。

修复:确保Record具有正确的复制构造函数,复制赋值和析构函数(甚至可以移动c'tor并移动赋值)(规则为5)。 这同样适用于HashTable类。

更好的修复:使用std::unordered_map