C ++ - copy-on-write基本实现

时间:2015-04-22 20:45:41

标签: c++ copy-on-write

我们应该在学校项目中使用copy-on-write。我一直在尝试一个非常简单的课程,但没有运气。我有这个:

#include <iostream>
#include <cstdlib>
#include <cstring>

using namespace std;

class CPerson {
   public:
            CPerson ();
            CPerson (const CPerson&);
           ~CPerson (); 
      char* m_name;
      char* m_surname;
      int   m_refs;

      void  rename (const char*, const char*);
};

CPerson :: CPerson () : m_name(NULL), m_surname(NULL), m_refs(1) {}

CPerson :: CPerson (const CPerson& src) : m_name (src.m_name), m_surname (src.m_surname), m_refs(src.m_refs+1) {} // supposed to be a shallow copy

CPerson :: ~CPerson () {
   if (m_refs == 1) {
      delete [] m_name;
      delete [] m_surname;
   }
   else --m_refs;
}

void CPerson :: rename (const char* name, const char* surname) {
   delete [] m_name;
   delete [] m_surname;

   m_name = new char [strlen(name)+1];
   m_surname = new char [strlen(surname)+1];

   strcpy (m_name, name);
   strcpy (m_surname, surname);
}

int main () {
   CPerson a;
   a.rename ("Jack", "Smith");
   cout << a.m_name << " " << a.m_surname << endl;

   CPerson b(a);
   cout << a.m_name << " " << a.m_surname << endl;
   cout << b.m_name << " " << b.m_surname << endl;
   // good so far...

   a.rename ("John", "Anderson"); // should rename both 'a' and 'b'
   cout << a.m_name << " " << a.m_surname << endl;
   cout << b.m_name << " " << b.m_surname << endl;   

   // prints random values



   return  0;
}

这很奇怪,因为当我拿出couts时,一切正常(没有泄漏,没有valgrind的错误)。

任何帮助都将不胜感激。

1 个答案:

答案 0 :(得分:2)

您的设计存在缺陷。您需要在字符数据本身上实现引用计数,而不是在单个CPerson对象上实现,因为它们不会相互共享一个引用计数变量。

尝试更像这样的东西:

#include <iostream>
#include <string>

using namespace std;

struct SPersonData
{
    string m_name;
    string m_surname;
    int m_refcnt;

    SPersonData() : m_refcnt(0) {}

    void incRef() { ++m_refcnt; }
    void decRef() { if (--m_refcnt == 0) delete this; }
};

class CPerson
{
private:
    SPersonData *m_data;

public:
    CPerson ();
    CPerson (const CPerson&);
    ~CPerson (); 

    CPerson& operator= (const CPerson&);

    string getName() const;
    string getSurname() const;

    void rename (const string&, const string&);
};

CPerson::CPerson ()
    : m_data(NULL) {}

CPerson::CPerson (const CPerson& src)
    : m_data (src.m_data)
{
    if (m_data) m_data->incRef();
}

CPerson::~CPerson ()
{
    if (m_data) m_data->decRef();
}

CPerson& operator= (const CPerson &src)
{
    if (this != &src)
    {
        if (m_data) m_data->decRef();
        m_data = src.m_data;
        if (m_data) m_data->incRef();
    }    
    return *this;
}

string CPerson::getName() const
{
    if (m_data) return m_data->m_name;
    return string();
}

string CPerson::getSurname() const
{ 
    if (m_data) return m_data->m_surname;
    return string();
}

void CPerson::rename (const string &name, const string &surname)
{
    if ((m_data) && (m_data->m_refcnt > 1))
    {
        m_data->decRef(); 
        m_data = NULL;
    }

    if (!m_data)
    {
        m_data = new SPersonData;
        m_data->incRef();
    }

    m_data->m_name = name;
    m_data->m_surname = surname;
}

使用std::shared_ptr来管理引用计数,可以在C ++ 11及更高版本中大大简化:

#include <iostream>
#include <string>
#include <memory>

using namespace std;

struct SPersonData
{
    string m_name;
    string m_surname;
};

class CPerson
{
public:
    shared_ptr<SPersonData> m_data;

    string getName() const;
    string getSurname() const;

    void rename (const string&, const string&);
};

string CPerson::getName() const
{
    if (m_data) return m_data->m_name;
    return string();
}

string CPerson::getSurname() const
{ 
    if (m_data) return m_data->m_surname;
    return string();
}

void CPerson::rename (const string &name, const string &surname)
{
    if (!((m_data) && m_data.unique()))
        m_data = make_shared<SPersonData>(); 

    m_data->m_name = name;
    m_data->m_surname = surname;
}

无论哪种方式,您的测试将如下所示:

int main ()
{
    CPerson a;
    a.rename ("Jack", "Smith");
    cout << a.getName() << " " << a.getSurname() << endl;

    CPerson b(a);
    cout << a.getName() << " " << a.getSurname() << endl;
    cout << b.getName() << " " << b.getSurname() << endl;
    // good so far...

    a.rename ("John", "Anderson"); // should rename only 'a' not 'b'
    cout << a.getName() << " " << a.getSurname() << endl;
    cout << b.getName() << " " << b.getSurname() << endl;   

    return  0;
}