Определить вручную только часть конструктора копирования и оператора присваивания

Мне интересно, есть ли способ реализовать конструкторы копирования и операторы присваивания так, чтобы при их переопределении для класса требовалась лишь небольшая модификация.

Например, рассмотрим класс как таковой:

class Foo {
private:
int* mInt_ptr;
/* many other member variables
of different types that aren't
pointers */
public:
Foo();
Foo(const Foo&);
Foo& operator=(const Foo&);
~Foo();
};

Теперь для того, чтобы разобраться с указателем mInt_ptr Я должен был бы обработать это соответствующим образом в конструкторе копирования и операторе присваивания. Тем не менее, остальные переменные-члены безопасны для поверхностного копирования. Есть ли способ сделать это автоматически?

Как только класс становится большим, он может стать утомительным и громоздким для явного выписывания операций для копирования переменных, не являющихся указателями, поэтому мне интересно, есть ли способ написать, скажем, конструктор копирования, такой как:

Foo::Foo(const Foo& tocopy)
{
mInt_ptr = new int(*tocopy.mInt_ptr);
/* Do shallow copy here somehow? */
}

а не в явной форме:

Foo::Foo(const Foo& tocopy)
{
mInt_ptr = new int(*tocopy.mInt_ptr);
mVar1 = tocopy.mVar1;
mVar2 = tocopy.mVar2;
...
...
mVarN = tocopy.mVarN;
}

0

Решение

Как правило, не используйте сырые указатели, именно по той причине, с которой вы сейчас боретесь. Вместо этого используйте подходящий интеллектуальный указатель и используйте назначение копирования-замены:

class Foo
{
int a;
Zip z;
std::string name;
value_ptr<Bar> p;

public:
Foo(Foo const &) = default;

Foo & operator=(Foo rhs)
{
rhs.swap(*this);
return *this;
}

void swap(Foo & rhs)
{
using std::swap;
swap(a, rhs.a);
swap(z, rhs.z);
swap(name, rhs.name);
swap(p, rhs.p);
}
};

namespace std { template <> void swap<Foo>(Foo & a, Foo & b) { a.swap(b); } }

value_ptr может быть полноценная реализация, или что-то простое, например:

template <typename T>    // suitable for small children,
class value_ptr          // but not polymorphic base classes.
{
T * ptr;

public:
constexpr value_ptr() : ptr(nullptr) { }
value_ptr(T * p) noexcept : ptr(p) { }
value_ptr(value_ptr const & rhs) : ptr(::new T(*rhs.ptr)) { }
~value_ptr() { delete ptr; }
value_ptr & operator=(value_ptr rhs) { rhs.swap(*this); return *this; }
void swap(value_ptr & rhs) { std::swap(ptr, rhs.ptr); }

T & operator*() { return *ptr; }
T * operator->() { return ptr; }
};
4

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

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

class Foo {
private:
int* mInt_ptr;
struct helper_t
/* many other member variables
of different types that aren't
pointers */
} mHelper;
public:
Foo();
Foo(const Foo&);
Foo& operator=(const Foo&);
~Foo();
};

Foo::Foo(const Foo& tocopy)
{
mInt_ptr = new int(*tocopy.mInt_ptr);
mHelper = tocopy.mHelper;
}

Использование лучших примитивов, как предположил Керрек, кажется более удачным дизайном. Это просто еще одна возможность.

2

Независимо от того, используете ли вы необработанные указатели или умные указатели, решение Kerrek является правильным в том смысле, что вы должны создать конструктор копирования, деструктор и обменяться и реализовать присваивание, используя такие:

class Foo
{
private:
int* mInt_ptr;
// many other member variables
// of different types
public:
Foo()
: mInt_ptr(NULL)
// initialize all other members
{}
Foo(const Foo& that)
: mInt_ptr(new int(*that.mInt_ptr) )
// copy-construct all other members
{}
Foo& operator=(const Foo& that)
{
// you may check if(this == &that) here
Foo(that).swap(*this);
return *this;
}
~Foo()
{
delete mInt_ptr;
// and release other resources
}
void swap(Foo& that)
{
std::swap(mInt_ptr, that.mInt_ptr);
// swap all members
}
};

Члены встроены здесь только для того, чтобы сохранить их компактность, обычно не желательно обременять определение класса встроенными определениями членов.

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector