拷贝构造函数和赋值运算符的正确写法

Correct way to write copy constructor and assignment operator

我有一个数据结构,我有函数 Ins(key, value) 将它们插入到数据结构中。我正在尝试编写复制构造函数和赋值运算符。我不知道只有这部分代码是否足够我还可以添加其余部分。这段代码不知何故有错误。

问题出在 Ins(temp -> m_Key, temp -> m_Val);两次。

CHash(const CHash & src)
{
  m_Table = new TItem * [src.m_Size];
  TItem * temp = src.m_FirstOrder;
  while (temp != NULL)
  {
    Ins(temp -> m_Key, temp -> m_Val);
    temp = temp -> m_NextOrder;
  }
}

CHash & operator = (const CHash & src)
{
  TItem * temp = m_FirstOrder;
  while (temp != NULL)
  {
    TItem * tmp = temp;
    temp = temp -> m_NextOrder;
    delete tmp;
  }
  delete [] m_Table;

  m_Table = new TItem * [src.m_Size];
  TItem * tmp1 = src.m_FirstOrder;
  while (tmp1 != NULL)
  {
    Ins(tmp1 -> m_Key, tmp1 -> m_Val);
    tmp1 = tmp1 -> m_NextOrder;
  }
}

我在 main 中尝试这个并遇到段错误。

CHash hashtable(100);
CHash hash2(50);
hash2 = hashtable;
hashtable.printAll();
hash2.printAll();
CHash b(hash2);
b.printAll();

这是完整的代码:

#include <string>
#include <iostream>
#include <bits/stdc++.h>
using namespace std;

struct TItem {
    TItem(string key, string val, TItem* nextHash,TItem* nextOrd, TItem * prevOrd)
    :m_Key(key),m_Val(val),m_NextHash(nextHash),m_NextOrder(nextOrd),m_PrevOrder(prevOrd){}

    string m_Key,m_Val;
    TItem * m_NextHash, * m_NextOrder, * m_PrevOrder;
};

class CHash{
public:
    CHash (int m) : m_Table(NULL),m_Size(m),m_FirstOrder(NULL),m_LastOrder(NULL)
    {
      m_Table = new TItem * [m];
      for (int i = 0; i < m; i++)
        m_Table[i] = NULL;
    }

    ~CHash()
    {
      TItem * temp = m_FirstOrder;
      while (temp != NULL)
      {
        TItem * tmp = temp;
        temp = temp -> m_NextOrder;
        delete tmp;
      }
      delete [] m_Table;
    }

    bool IsSet(string key)
    {
      TItem * temp = m_Table[hashFn(key)];
      if (temp == NULL)
      {
        return false;
      }
      while (temp != NULL)
      {
        if (temp -> m_Key == key)
        {
          return true;
        }
        temp = temp -> m_NextHash;
      }
      return false;
    }

    CHash(const CHash & src)
    {
      m_Size = src.m_Size;
      m_Table = new TItem * [src.m_Size];
      for (int i = 0; i < src.m_Size; i++)
        m_Table[i] = NULL;
      TItem * temp = src.m_FirstOrder;
      while (temp != NULL)
      {
        Ins(temp -> m_Key, temp -> m_Val);
        temp = temp -> m_NextOrder;
      }
    }

    CHash & operator = (const CHash & src)
    {
      m_Size = src.m_Size;
      TItem * temp = m_FirstOrder;
      while (temp != NULL)
      {
        TItem * tmp = temp;
        temp = temp -> m_NextOrder;
        delete tmp;
      }
      delete [] m_Table;

      m_Table = new TItem * [src.m_Size];
      for (int i = 0; i < src.m_Size; i++)
        m_Table[i] = NULL;
      TItem * tmp1 = src.m_FirstOrder;
      while (tmp1 != NULL)
      {
        Ins(tmp1 -> m_Key, tmp1 -> m_Val);
        tmp1 = tmp1 -> m_NextOrder;
      }
    }

    bool Ins(const string & key, const string & val)
    {
      string help = key;
      if (IsSet(help))
        return false;
      //first element added ever
      if (m_FirstOrder == NULL)
      {
        TItem * tmp01 = new TItem(key, val, NULL, NULL, NULL);
        m_Table[hashFn(help)] = tmp01;
        m_FirstOrder = m_LastOrder = tmp01;
      }
      else
      {
        TItem * temp = m_Table[hashFn(help)];
        //first added to that hash
        if (temp == NULL)
        {
          TItem * tmp02 = new TItem(key, val, NULL, NULL, m_LastOrder);
          m_Table[hashFn(help)] = tmp02;
          m_LastOrder -> m_NextOrder = tmp02;
          m_LastOrder = tmp02;
        }
        else
        {
          while (temp -> m_NextHash != NULL)
          {
            temp = temp -> m_NextHash;
          }
          TItem * tmp03 = new TItem(key, val, NULL, NULL, m_LastOrder);
          temp -> m_NextHash = tmp03;
          m_LastOrder -> m_NextOrder = tmp03;
          m_LastOrder = tmp03;
        }
      }
      return true;
    }

    bool Del (const string & key)
    {
      string help = key;
      if (!IsSet(help))
        return false;

      TItem * temp = m_Table[hashFn(help)];

      if (temp == NULL)
        return false;


      while (temp != NULL)
      {
        if (temp -> m_Key == key)
          break;
        temp = temp -> m_NextHash;
      }

      if (temp == NULL)
        return false;

      if (m_FirstOrder == temp)
        m_FirstOrder = m_FirstOrder -> m_NextOrder;
      else
        temp -> m_PrevOrder -> m_NextOrder = temp -> m_NextOrder;
      if (m_LastOrder == temp)
        m_LastOrder = m_LastOrder -> m_PrevOrder;

      m_Table[hashFn(help)] = temp -> m_NextHash;
      delete temp;
      return true;
    }

    template <typename func>
    void ForEach(func f)
    {

    }
    void printAll()
    {
      cout << "PRINTING" << endl;
      TItem * temp = m_FirstOrder;
      while (temp != NULL)
      {
        cout << "  :  " << temp -> m_Val << " : ";
        temp = temp -> m_NextOrder;
      }
      cout << endl;
    }

private:
    TItem **        m_Table;
    unsigned int    m_Size;
    TItem *         m_FirstOrder, * m_LastOrder;
    unsigned int hashFn(string & str){
        std::hash<std::string> hash_fn;
        return hash_fn(str)%m_Size;
    }
};

int main(int argc, char** argv) {

    CHash hashtable(100);
    CHash hash2(50);
    hashtable.Ins("h1","car");
    assert ( !hashtable.Ins("h1","phone"));
    hashtable.Ins("h2","field");
    hashtable.Ins("h3","house");
    hashtable.Ins("h4","tree");
    hashtable.printAll();
    assert ( hashtable.Del("h3") );
    assert ( !hashtable.Ins("h4","tree") );
    assert(!hashtable.Del("h4d"));
    assert(hashtable.Del("h1"));
    assert(!hashtable.Del("h3"));
    assert(!hashtable.Del("h1"));
    hash2 = hashtable;
    hashtable.printAll();
    hash2.printAll();
    CHash b(hash2);
    b.printAll();


    hashtable.ForEach([](TItem * it ){
        cout<<it->m_Key<<" - "<<it->m_Val<<endl;
    });

    return 0;
}

我运行它是这样的:g++ -std=c++11 -g filename.cpp

我发现你的代码有几个问题。

  1. 赋值运算符不 return *this.
  2. 复制构造函数没有初始化 m_FirstOrderm_LastOrder
  3. 大量复制粘贴使代码难以阅读并且error-prone。

如果我们解决问题,我们会得到:

class CHash{
public:
    CHash (int m)
    {
      initialize(m);
    }

    ~CHash()
    {
      finalize();
    }

    CHash(const CHash & src)
    {
      initialize(src.m_Size);
      copy(src.m_FirstOrder);
    }

    CHash & operator = (const CHash & src)
    {
      if (&src == this)
        return;
      finalize();
      initialize(src.m_Size);
      copy(src.m_FirstOrder);
      return *this;
    }

    ...

private:
    ...

    void initialize(int m)
    {
      m_Size = m;
      m_Table = new TItem * [m];
      for (int i = 0; i < m; i++)
        m_Table[i] = NULL;
      m_FirstOrder = NULL;
      m_LastOrder  = NULL;
    }

    void finalize()
    {
      TItem * temp = m_FirstOrder;
      while (temp != NULL)
      {
        TItem * tmp = temp;
        temp = temp -> m_NextOrder;
        delete tmp;
      }
      delete [] m_Table;
    }

    void copy(TItem *src)
    {
      while (src != NULL)
      {
        Ins(src -> m_Key, src -> m_Val);
        src = src -> m_NextOrder;
      }
    }
};

不过我想知道,为什么你不使用 nullptr 而不是 NULLstd::vector<TItem*> 而不是 TItem**...