C ++, как переместить объект в nullptr - PullRequest
1 голос
/ 03 апреля 2019

Я думаю о странном случае использования, когда я хочу переместить объект в nullptr. Может быть, я должен дать фрагмент кода:

class Objpair {
   public:
      Objpair(Obj&& a, Obj&&b) : first(&a), second(&b) { }
   private:
       Obj* first;
       Obj* second;
};

Проблема в том, что когда a и b находятся вне области видимости, первый и второй указатели будут зависать. Если я смогу переместить Object a на первый указатель, тогда не должно быть проблем с двойным освобождением и проблемами с областями видимости. Если бы член сначала был объявлен как Obj, а не как указатель Obj *, то прямое первое (std :: move (a)) сделало бы эту работу. Я должен делать что-то не так здесь. Я думаю о перемещении, а не о копировании, потому что я пытаюсь передать управление от другого объекта текущему объекту и улучшить производительность.

Версия указателя используется, потому что я думаю о полиморфном поведении объекта-члена.

Ответы [ 4 ]

5 голосов
/ 03 апреля 2019

Что вы можете сделать, это переместить конструкцию объектов из ваших параметров, например:

class Objpair {
   public:
      Objpair(Obj&& a, Obj&& b)
      : first(new Obj(std::move(a)))
      , second(new Obj(std::move(b)))
      {}

   private:
       Obj* first;
       Obj* second;
};

Я рекомендую использовать std::unique_ptr:

class Objpair {
   public:
      Objpair(Obj&& a, Obj&& b)
      : first(std::make_unique<Obj>(std::move(a)))
      , second(std::make_unique<Obj>(std::move(b)))
      {}

   private:
       std::unique_ptr<Obj> first;
       std::unique_ptr<Obj> second;
};
2 голосов
/ 03 апреля 2019

В комментарии вы сказали,

первый и второй могут указывать на BaseClass, Derived1, Derived2,

В этом случае лучшим вариантом будет предоставить virtual функцию-член в Obj для клонирования объекта.

class Obj
{
   public:
      virtual Obj* clone() const = 0;
   ...
}

и использовать его для инициализации first и second.

 class Objpair {
   public:
      Objpair(Obj&& a, Obj&&b) : first(a.clone()), second(b.clone()) { }
   private:
       Obj* first;
       Obj* second;
};

и убедитесь, что вы позаботились об освобождении памяти, указанной в деструкторе first и second. См. Правило трех для получения дополнительной информации.

Вы можете использовать умные указатели, чтобы исключить потребность в коде в вашем классе для управления временем жизни объектов. Используете ли вы std::unique_ptr или std::shared_ptr, зависит от того, как вы хотите использовать Objpair.

2 голосов
/ 03 апреля 2019

Вы не можете перемещать объекты по указателям (nullptr или иначе).Вы также не можете продлить срок жизни объектов, указав на них указатели - C ++ просто не работает таким образом.

Поскольку вам нужно полиморфное поведение, вы не можете просто скопировать-конструировать или назначить Objуказатель.Вы, вероятно, захотите что-то вроде clone() метода для класса Obj, как обсуждалось в этом вопросе:

Копирование производных сущностей с использованием только указателей базового класса (без исчерпывающего тестирования!) - C ++

Подробнее об этом.Помните, однако: вы не можете продлить время жизни ссылки на rvalue, поэтому вам нужно каким-то образом создать новый объект.Также помните, чтобы избежать утечки памяти.Поэтому, если ваш clone() выдает Obj*, вам нужно написать что-то вроде:

class Objpair {
   public:
      Objpair(Obj&& a, Obj&&b) : 
         first(a.clone()), 
         second(b.clone()) 
      { }
   private:
      std::unique_ptr<Obj> first;
      std::unique_ptr<Obj> second;
};

на std::shared_ptr вместо unique_ptr (см. этот ответ о выборе между двумя).

Если вам нужно "полиморфное" поведение, но вы знаете точные типыa и b во время компиляции, и вы не против иметь разные ObjPair классы, вы также можете попробовать что-то вроде этого:

template <typename T1, typename T2>
class Objpair {
   public:
      Objpair(T1&& a, T2&&b) : first(a), second(b) 
      { }
   private:
      T1 first;
      T2 second;
};

, и в этом случае не используются указатели (!)

0 голосов
/ 03 апреля 2019

@ Хорошо принятый ответ Галика. Хотя требуется, чтобы Obj был по крайней мере подвижным, что аналогично другим ответам с Clone() решениями - все они предъявляют некоторые требования к Obj и его потомкам. Чтобы снять это ограничение, мы можем изменить конструктор Objpair на std::unique_ptr<Obj>:

#include <memory>

class Obj
{
public:
    Obj() {}

    // non-copyable/non-movable
    Obj(const Obj&) = delete;
    Obj& operator=(const Obj&) = delete;
};

class Objpair {
   public:
      Objpair(std::unique_ptr<Obj>&& a, std::unique_ptr<Obj>&& b)
      : first(std::move(a))
      , second(std::move(b))
      {}

   private:
       std::unique_ptr<Obj> first;
       std::unique_ptr<Obj> second;
};

int main()
{
    Objpair p{std::make_unique<Obj>(), std::make_unique<Obj>()};
    return 0;
}

Возможные бонусы:

  • пользователь имеет дело с распределением памяти, и поэтому имеет лучший контроль над ним.
  • Пользователь может использовать экземпляры a и b параметров, прежде чем передать их в Objpair, полиморфно.
...