Явный вызов конструктора копирования объекта в unique_ptr - PullRequest
0 голосов
/ 08 декабря 2018

Я использую идиому pimpl с const std::unique_ptr для хранения реализации класса.Мой класс должен поддерживать конструкцию копирования и назначение копирования.Что я хотел бы сделать, так это вручную вызвать конструктор копирования класса impl внутри unique_ptr.Однако я не вижу, как.

#include <memory>

struct potato {
    potato();
    ~potato();
    potato(const potato& p);

private:
    struct impl;
    const std::unique_ptr<impl> _pimpl;
};

struct potato::impl {
    int carbs = 42;
};

potato::potato()
        : _pimpl(std::make_unique<impl>()) {
}

potato::~potato() = default;

potato::potato(const potato& p) {
    // Try to call the copy constructor of impl, stored in unique_ptr, not the
    // unique_ptr copy-constructor (which doesn't exist).
    _pimpl.get()->impl(p._pimpl); // This doesn't work.
}

Я проверил еще один вопрос о явном вызове конструктора копирования для объекта.В одном ответе рекомендуется использовать новое размещение.

Object dstObject;
new(&dstObject) Object(&anotherObject);

Могу ли я использовать это в своем конструкторе копирования?Если да, то как?Я не очень понимаю, что там происходит.Спасибо.

Ответы [ 2 ]

0 голосов
/ 08 декабря 2018

Вы можете явно вызывать конструкторы в неинициализированном хранилище с помощью оператора размещения new, как вы упоминали.Вы можете вернуть хранилище объекта в неинициализированное состояние, явно вызвав его деструктор.

Вот простая реализация оператора присваивания, который явно вызывает конструктор копирования и деструктор, который вы уже определили как часть вашего интерфейса:

#include <new>

potato& potato::operator=(const potato& x)
{
  if ( this != &x ) { // check for self-assignment!
    this->~potato();
    new(this) potato(x);
  }

  return *this;
}

Возможно, вы также захотите определить конструктор перемещения и перегрузить оператор присваивания, когда правая часть является временной.То есть перегрузка для potato&& src, а также const potato& src.Вы можете использовать идиому подкачки, если ваш класс поддерживает его, или тот же код, что и выше, но вызывать new(this) potato(std::move(src));.

Если у вас есть доступ к деструктору и конструктору копирования класса, заключенному в умный указатель,вы можете сделать то же самое с ними, просто разыменовывая умные указатели.Вы, вероятно, не хотите, однако.

Конструктор копирования по умолчанию и оператор присваивания должны работать очень хорошо, если содержимое класса - умные указатели, контейнеры STL и так далее.Возможно, вы захотите скопировать данные, на которые ссылаются умные указатели, написав такие вещи, как *p = x или *p = *q или std::swap, а не явные вызовы конструктора копирования.

0 голосов
/ 08 декабря 2018

Что я хотел бы сделать, так это вручную вызвать конструктор копирования класса impl внутри unique_ptr

Здесь кроется ваша ошибка.Поскольку вы находитесь внутри (копирования) конструктора potato, нет уже построенного объекта impl, над которым вам нужно было бы «вручную» вызывать конструктор копирования.

Просто создайте свой новый implпередав ему ссылку на оригинал impl для копирования.

potato::potato(const potato& p)
    : _pimpl(std::make_unique<impl>(*p._pimpl) {
}

Что касается присвоения, вы можете легко переадресовать на оператор присвоения impl:

potato &operator=(const potato &p) {
    *_pimpl = *p._pimpl;
    return *this;
}
...