C ++, возвращающий объект, бросает интерфейс - PullRequest
1 голос
/ 11 июля 2011

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

Цель перед тем, как иметь интерфейс:

class C {
  public:
  int get() { return v; }
  void set(int value) { v = value; }
  C operator * (C &l) {
    C r;
    r.set(get() * l.get());
    return r;
  }

  private:
  int v;
};

В этом случае метод operator * возвращает объект типа C, но не ссылку (имейте это в виду),Он использует преимущества оптимизации возвращаемого значения.Теперь давайте подойдем к проблеме: в этом случае я хочу разработать интерфейс:

struct I {
  virtual int get() = 0;
  virtual void set(int value) = 0;
  virtual I& operator * (I &l) = 0;
};

В этом случае я не могу использовать возвращаемый тип I (по значению) для метода оператора *,потому что я абстрактен.Хорошо, давайте перейдем к проблеме:

class C : public I {
  public:
  int get() { return v; }
  void set(int value) { v = value; }
  I& operator * (I &l) {
    C r;
    r.set(get() * l.get());
    return r; //  warning: reference to local variable ‘r’ returned
  }

  private:
  int v;
};

Проблема здесь в том, что я возвращаю ссылку на локальную переменную.

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

Заранее спасибо.

Ответы [ 5 ]

2 голосов
/ 11 июля 2011

Вы не можете сделать это таким полиморфным способом без использования динамического выделения.

Вы уверены, что умножение действительно имеет смысл полиморфным способом? Если это так, вам придется возвращать динамически распределенный указатель и управлять его временем жизни (возможно, с помощью интеллектуального указателя). Но в этом случае он не соответствует нормальной семантике, если operator*, поэтому вам, вероятно, следует создать специальную функцию для этой работы.

Также обратите внимание, что у вас есть несколько причуд, которые не являются C ++ - идиоматическими. get должен быть методом const, параметр вашего operator* должен быть по константной ссылке или значению (никто не хочет operator* изменить один из его операндов, так как это нарушает принцип наименьшего удивления). И, как правило, operator* возвращает свой результат по значению, чтобы избежать путаницы в отношении того, кому принадлежит выделенная возвращенная память, если оператор не вернул по значению.

2 голосов
/ 11 июля 2011

Либо верните указатель на динамически размещенный объект (I*), либо верните C по значению.

Возврат ссылки возможен, но это сбивает с толку, поэтому не делает следующее:

virtual I &operator*(I &l) {
    C *r = new C();
    r->set(get() * l.get());
    return *r; //  warning: reference to local variable ‘r’ returned
}

Проблема здесь в том, что, хотя возвращаемое значение выглядит как значение, оно все равно должно быть delete 'd с необычным синтаксисом delete &r;

1 голос
/ 11 июля 2011

Я полагаю, что в этом случае вы должны использовать operator*=, а не operator*.

В operator*= вы возвращаете ссылку на *this, которая не является локальной переменной, поэтому ее можно вернуть.

В случае operator* оно должно быть постоянным, и вам нужно возвращать по значению (ну, язык не заставляет вас это делать, но если вы делаете умножение, скорее всего, это наиболее разумная вещь для делать). Я не уверен, как заставить такую ​​вещь работать с виртуальными функциями, я обычно использовал бы шаблоны (хотя при таком подходе вы теряете полиморфизм времени выполнения).

0 голосов
/ 11 июля 2011

1 - Вы не можете вернуть экземпляр объекта, типизированный как 'I' (абстрактный тип)
2 - Если вы хотите вернуть объект, а не указатель, вы должны использовать нормальный тип возврата ('C').Другой способ: сделать «I» не абстрактным и вернуть «I».
3 - Рекомендуемый способ: вернуть только I * и не забудьте удалить объект позже.

0 голосов
/ 11 июля 2011

Редактировать: то, что вы хотите, возможно ... однако это действительно плохая идея.

Требование отсутствия выделения памяти категорически невозможно. Умножение должно создать новый объект (по определению), а полиморфный объект должен быть создан в куче (если вы хотите избежать нарезки и обращаться к нему через интерфейс).

Однако, если вы все еще хотите написать I* result = a*b*c с полиморфным a,b,c, вы можете сделать это с помощью прокси-объекта и использовать неявное приведение ..

Вы можете создать прокси-объект с добавлением неявного приведения:

   struct Iref {
      I* ref;
      Iref(I* aref):ref(aref){};
      operator I*() { return ref; }   
      Iref operator * (I& b);
      Iref operator * (Iref b);
      Iref operator * (I* b);
   };

С этим прокси и подходящим образом написанными методами вы можете получить:

int main()
{
  C a(1), b(2), c(3);
  I* result = a*b*c;
  return 0;
}

Остается проблема с утечкой памяти. Либо вы используете сборщик мусора с вашим приложением. У меня нет опыта с этим, я просто знаю, что это возможно. Или вы позволяете прокси-объекту обрабатывать освобождение памяти.

Этот довольно сложный трюк должен показать, как operator* и полиморфизм вместе не очень хорошая идея . Существуют и другие проблемы, так как при полиморфизме вы можете получить a * b и b * a с разными результатами.

...