Implementation note
That Expr member called e in class UnaryExpr is magic in several ways
- Using Expr::Expr(const Expr&) to initialize it will take care of memory management
- Calling e.print will result in appropriate virtual calls automatically
- Destroying the surrounding UnaryExpr will destroy e appropriately