C ++ - 在函数中创建指向超类匹配子类参数的指针

时间:2016-05-15 10:51:04

标签: c++ oop pointers

我在询问之前已经搜索过但是没有发现任何可以解决我问题的事情。

我想创建一个指向超类(实际上总是引用其中一个子类)的指针,在函数中匹配子类参数(指针或const引用)。

上下文:用c ++创建一个“高级”计算器。

让我向您详细介绍本期本期使用的课程:

首先我们有文字:

class Literal {};
class IntegerLiteral : public Literal {};
class RealLiteral : public Literal {};
class RationalLiteral : public Literal {};
//....

我们有一个堆栈,用于通过存储地址来保存Literal对象

// If st is instance of stack then :
st.top(); // returns a Literal*

我们有通过取消堆叠正确数量的Literal *(取决于运算符的arity)与堆栈交互的Operator对象,在Literal *对象上应用运算符,最后堆叠结果。

class Operator {
  int n; // operator arity
public:
  virtual void executeOperator(stack *st) = 0; //
};

运算符子类之一(例如):

class PlusOperator : public Operator {
public:
  virtual void execute(StackUTComputer *st) override {
      Literal* arg1 = st->top();
      Literal* arg2 = st->top();
      Literal* result = applyOperator(arg1, arg2);
      st->pop(); st->pop();
      st->push(result);
  }

  Literal* execute(IntegerLiteral* a, IntegerLiteral* b) {
      return new IntegerLiteral(a->getValue() + b->getValue());
  }

  Literal* execute(IntegerLiteral* a, RealLiteral* b) {
      return new RealLiteral(a->getValue() + b->getValue());
  }


  Literal* execute(IntegerLiteral* a, RationalLiteral* b) {
      return new RationalLiteral(
           a->getValue() + (a->getValue()*b->getDenominator()),
           b->getDenominator()
      );
  }

  // ...
};

我的目的(通过重载函数applyOperator)是“神奇地”让计算机知道哪个函数调用取决于操作符未被堆叠的文字的真实类型(类Literal是抽象的:栈将总是包含细节Literal的子类。)

但它不按我想要的方式工作。 我的意思是,调用applyOperator(arg1, arg2)arg1arg2为Literal *)无效,因为没有与签名匹配的函数。

我知道我在通常使用的方式中使用c ++多态性(这是一个带有超类参数的函数的子类参数)。

我不知道如何扭转我的架构以正确使用多态性,也许有一些语法有用的解决方案,以使我的想法有效。

无论哪种方式,我都很感谢你的帮助!!

圣拉斐尔。

2 个答案:

答案 0 :(得分:1)

有一种方法可以按预期使用多态(没有dynamic_cast):

#include <iostream>
#include <memory>
#include <string>

struct IntegerLiteral;
struct RealLiteral;

struct Literal {
    virtual void add(const Literal &) = 0;
    virtual void add(const IntegerLiteral &) = 0;
    virtual void add(const RealLiteral &) = 0;

    virtual void add_to(Literal &) const = 0;
    virtual void add_to(IntegerLiteral &) const = 0;
    virtual void add_to(RealLiteral &) const = 0;
    virtual std::ostream &print(std::ostream &os) const = 0;
    virtual ~Literal() = default;
};

std::ostream &operator<<(std::ostream &os, const Literal &l) {
    return l.print(os);
}

struct IntegerLiteral : Literal {
    IntegerLiteral(int i)
        : i(i) {}
    int i = 0;
    void add(const Literal &other) override {
        //now we know one operand is an IntegerLiteral and can pass on that information to the other Literal
        other.add_to(*this);
    }
    void add(const IntegerLiteral &other) override {
        i += other.i;
    }
    void add(const RealLiteral &other) override;
    void add_to(Literal &other) const override {
        other.add(*this);
    }
    void add_to(IntegerLiteral &other) const override {
        other.i += i;
    }
    void add_to(RealLiteral &other) const override;
    std::ostream &print(std::ostream &os) const override {
        return os << i;
    }
};

struct RealLiteral : Literal {
    RealLiteral(double d)
        : d(d) {}
    double d = 0;
    void add(const Literal &other) override {
        other.add_to(*this);
    }
    void add(const IntegerLiteral &other) override {
        d += other.i;
    }
    void add(const RealLiteral &other) override {
        d += other.d;
    }
    void add_to(Literal &other) const override {
        other.add(*this);
    }
    void add_to(IntegerLiteral &other) const override {
        //now we know both operands and can do the calculation
        other.i += d;
    }
    void add_to(RealLiteral &other) const override {
        other.d += d;
    }
    std::ostream &print(std::ostream &os) const override {
        return os << d;
    }
};

void IntegerLiteral::add(const RealLiteral &other) {
    i += other.d;
}

void IntegerLiteral::add_to(RealLiteral &other) const {
    other.d += i;
}

int main() {
    std::unique_ptr<Literal> l1 = std::make_unique<RealLiteral>(3.14);
    std::unique_ptr<Literal> l2 = std::make_unique<IntegerLiteral>(42);
    l1->add(*l2);
    std::cout << *l1 << '\n';
}

DEMO

你需要大量的代码来完成这项工作,每增加Literal次,每次运算符的次数都会变差,两次变差。此外,如果您忘记override函数,您可能会在运行时获得无限循环和堆栈溢出 一个更好的方法(更容易编写和更快的运行)就是只使用doubleBigNum来处理所有事情,而不用担心多态性。

答案 1 :(得分:0)

您正在混合使用ad hoc polymorphism(重载)和subtype polymorphism等不同概念,这些概念是通过虚拟表的后期方法绑定实现的。

基本上发生的是编译器选择在编译时调用哪个重载方法,而不是在运行时调用。如果不使用RTTI,这就不可能实现。

编译器无法确定哪两个Literal实例的类型,并且C ++在处理非虚方法时仅支持早期绑定。它在编译时唯一可以推断的是两个参数都是Literal*类型的事实,所以它只查找那个重载。

您需要一种动态切换来执行您想要的操作,这可以通过使用dynamic_cast(或类似的手工解决方案)来获得。