正确地进行运算符重载和多态性
doing operator overloading and polymorphism correctly
我有两个包装器 类 用于字符串和整数,一个用于表示二进制运算并重载运算符 <<
以将它们写入字符串格式。不幸的是,没有 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
一般来说,让运算符在同一个 class 中重载多态表明您正在混合使用值风格 classes 和身份风格 classes,并且因此有一种 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;
}
然后将实际的打印逻辑放入每个 class 的 print
覆盖中。
但我也觉得有必要指出您的 AST
class 似乎没有用。运算符和二进制表达式实际上没有任何共同点。说运算符或表达式是抽象语法树在概念上也是错误的。表达式是 AST 的 部分 ,树中的单个节点。运算符是表达式的一部分。整棵树都不一样。
您应该为 operator_
和 expr
设置单独的层次结构根。是的,这可能意味着您必须打印两次。值得。
我有两个包装器 类 用于字符串和整数,一个用于表示二进制运算并重载运算符 <<
以将它们写入字符串格式。不幸的是,没有 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
一般来说,让运算符在同一个 class 中重载多态表明您正在混合使用值风格 classes 和身份风格 classes,并且因此有一种 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;
}
然后将实际的打印逻辑放入每个 class 的 print
覆盖中。
但我也觉得有必要指出您的 AST
class 似乎没有用。运算符和二进制表达式实际上没有任何共同点。说运算符或表达式是抽象语法树在概念上也是错误的。表达式是 AST 的 部分 ,树中的单个节点。运算符是表达式的一部分。整棵树都不一样。
您应该为 operator_
和 expr
设置单独的层次结构根。是的,这可能意味着您必须打印两次。值得。