правильно выполнять перегрузку и полиморфизм операторов - PullRequest
0 голосов
/ 02 января 2019

У меня есть два класса-оболочки для string и int и один для представления двоичной операции и перегрузки оператора << для записи их в строковом формате.К сожалению, я не могу перегрузить << без функции friend и не могу получить полиморфизм без virtual и не могу использовать friend с virtual !!

#include <iostream>
#include <string>

class AST {};
class expr: public AST {};

class operator_: public AST {};

class BinOp: public expr {
private:
  expr *_left;
  expr *_right;
  operator_ *_op;
public:
  BinOp(expr *p_left, expr *p_right, operator_ *p_op):_left(p_left),_right(p_right),_op(p_op) {}
  friend std::ostream &operator<< (std::ostream &out, const BinOp &binop) {
    out << (binop._left) << " " << (binop._op) << " " << (binop._right);
  }
};

class LShift: public operator_ {
public:
  LShift() {}
  friend std::ostream &operator<< (std::ostream &out, const LShift &lshift) {
    out << "<<";
  }
};

class Name: public expr {
private:
  std::string _id;
public:
  Name(std::string p_id) { _id = p_id; }
  friend std::ostream &operator<< (std::ostream &out, const Name &name) {
    out << name._id;
  }
};

class Num: public expr {
private:
  int n;
public:
  Num(int p_n) { n = p_n; }
  friend std::ostream &operator<< (std::ostream &out, const Num &num) {
    out << num.n;
  }
};

int main() {

  auto name = Name("x");
  auto num  = Num(5);
  auto lshift = LShift();
  auto binop = BinOp(&name, &num, &lshift);

  std::cout << binop << '\n';

  return 0;
}

вышеупомянутая программа выводит указатель,

0x7ffd05935db0 0x7ffd05935d8b 0x7ffd05935d8c

, но вместо указателя мне нужны объекты, необходимые для печати. ​​

x << 5

1 Ответ

0 голосов
/ 02 января 2019

В общем, наличие перегрузки операторов полиморфизмом в одном и том же классе является признаком того, что вы смешиваете классы стиля значений и классы стиля идентичности и, следовательно, специфический для C ++ запах кода.

Но ясказал бы, что оператор вставки потока является исключением.

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

class AST {
public:
  virtual void print(std::ostream& s) const = 0;
  virtual ~AST() {} // you probably want this one too
};

// no need for friendliness
std::ostream& operator <<(std::ostream& s, const AST& node) {
  node.print(s);
  return s;
}

И тогда выпоместите фактическую логику печати в переопределения print каждого класса.

Но я также чувствую необходимость заметить, что ваш класс AST не кажется полезным.На самом деле нет ничего общего между оператором и двоичным выражением.Также концептуально неправильно говорить, что оператор или выражение - это абстрактное синтаксическое дерево.Выражение - это часть AST, единственного узла в дереве.Оператор является частью выражения.Все дерево - это нечто иное.

У вас должны быть отдельные корни иерархии для operator_ и expr.И да, это, вероятно, означает, что вам придется печатать дважды.Оно того стоит.

...