c : переместить оператор назначения и наследование

#c #inheritance #move-semantics #assignment-operator

#c #наследование #переместить-семантика #оператор присваивания

Вопрос:

Этот код компилируется и выполняется нормально:

 #include <iostream>

class Base
{
  public:
  Base(int value)
  : clean_(true)
  {
      value_ = new int;
      *value_ = value;
  }
  ~Base()
  {
      if(clean_)
        delete value_;
  }
  Base(Baseamp;amp; other) noexcept 
  : value_{std::move(other.value_)},
    clean_(true)
  {
      other.clean_=false;
  }
  Baseamp; operator=(Baseamp;amp; other) noexcept
  {
      value_ = std::move(other.value_);
      other.clean_=false;
      clean_=true;
  }
  void print()
  {
      std::cout << value_ << " : " << *value_ << std::endl;
  }
  
  int* value_;
  bool clean_;
    
};

class A : public Base 
{
  public:
  A(int v1, double v2) : Base(v1)
  {
      a_ = new double;
      *a_ = v2;
  }
  A(Aamp;amp; other) noexcept
  : Base(std::forward<Base>(other)),
    a_(std::move(other.a_))
  {}
  Aamp; operator=(Aamp;amp; other) noexcept
  {
      // should not the move assignment operator
      // of Base be called instead ? 
      // If so: how ?
      this->value_ = std::move(other.value_);
      other.clean_=false;
      this->clean_=true;
      a_ = std::move(other.a_);
  }
  void print()
  {
      std::cout << this->value_ << " "
                << *(this->value_) << " "
                << a_ << " " << *a_ << std::endl;
  }

  double* a_;
  bool clean_;
    
};

A create_a(int v1,double v2)
{
    A a(v1,v2);
    return a;
}

int main()
{
    Base b1(20);
    b1.print();
    
    Base b2 = std::move(b1);
    b2.print();
    
    A a1(10,50.2);
    a1.print();
    
    A a2 = std::move(a1);
    a2.print();
    
    A a3 = create_a(1,2);
    a3.print();
}
  

A является подклассом Base.

Код оператора присваивания перемещения A повторяет код оператора присваивания Base.

Есть ли способ избежать этой репликации кода?

Комментарии:

1. Использовать unique_ptr<int> и вообще не писать эти методы?

2. Собираетесь ли вы иметь две копии clean_ объектов типа A in?

3. В качестве примечания: вы знаете, что std::move не выполняет перемещение, но что это просто приведение? Так что a_ = std::move(other.a_); не имеет особого смысла, поскольку a_ это просто указатель.

4. @t.niese чтобы убедиться, что я понял, вы имеете в виду, что a_=std::move(other.a_) эквивалентно a_ = other.a_ ?

5. Для показанного кода результат эквивалентен. std::move это просто приведение к r-value , и значение other.a_ будет скопировано в обоих случаях в данном случае. Таким образом, вы можете просто написать a_ = other.a_ в этом случае.

Ответ №1:

Измените int* value_; на int value_; и double* a_; на double a_; , и вам больше не нужно писать какие-либо специальные функции-члены, поскольку предоставленные компилятором значения по умолчанию просто работают ™

Если вам действительно нужно динамическое выделение памяти, то используйте тип RAII, такой std::vector как std::unique_ptr , std::shared_ptr ,,, и т.д. на свое место, поскольку они предназначены для правильного копирования и / или перемещения.

Комментарии:

1. Для этого конкретного примера это имеет смысл, но это всего лишь минимальный код для прояснения вопроса. Как вы вызываете оператор назначения перемещения базового класса, учитывая, что этот последний не такой тривиальный, как приведенный здесь?

2. @Vince Хитрость заключается в использовании типов RAII. Тогда вам не нужно ничего делать, вам будет предоставлено правильное поведение. Если вы действительно хотите вызвать назначение перемещения базового класса, тогда сделайте Base::operator=(std::move(other)); , и это вызовет оператор присваивания перемещения базы.

3. спасибо за ответ, это сработало! (Мой исходный код манипулирует указателями на некоторую разделяемую память между процессами.)