базовая реализация копирования при записи

Мы должны использовать копирование при записи в нашем школьном проекте. Я экспериментировал с очень простым классом, но безуспешно. У меня есть это:

#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 valuesreturn  0;
}

Это странно, потому что, когда я вынимаю койки, все работает нормально (без утечек, без ошибок по valgrind).

Любая помощь будет оценена.

0

Решение

Ваш дизайн имеет недостатки. Вам нужно реализовать подсчет ссылок на самих символьных данных, а не на отдельных 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;
}

Что может быть значительно упрощено в C ++ 11 и более поздних версиях с помощью std::shared_ptr управлять количеством ссылок:

#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;
}
2

Другие решения


По вопросам рекламы [email protected]