C++ - 使指向父类(super class)的指针与函数中的子类参数匹配

标签 c++ oop pointers

我在询问之前进行了搜索,但没有找到任何解决我的问题的方法。

我想让一个指向父类(super class)的指针(实际上总是指代其中一个子类)与函数中的子类参数(指针或常量引用)相匹配。

上下文:用 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*

我们有 Operator 对象,它们将通过拆分正确数量的 Literal*(取决于运算符的元数)与堆栈交互,将运算符应用于 Literal* 对象并最终堆叠结果。

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

Operator 子类之一(例如):

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 是抽象的:堆栈将始终包含具体的 Literal 子类)。

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

我知道我以另一种通常使用的方式使用 c++ 多态性 int(即为采用父类(super class)参数的函数提供子类参数)。

我不知道如何改变我的架构以正确使用多态性,也许有一些语法上有用的解决方案可以让我的想法发挥作用。

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

拉斐尔。

最佳答案

有一种方法可以按预期使用多态性(无需 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 都会以二次方的方式变得更糟,而每个运算符都会使它变得更糟两倍。此外,如果您忘记覆盖一个函数,您可能会在运行时遇到无限循环和堆栈溢出。
一种更好的方法(更易于编写且运行速度更快)是只对所有内容使用 doubleBigNum,而不用担心多态性。

关于C++ - 使指向父类(super class)的指针与函数中的子类参数匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37237358/

相关文章:

c++ - 我想包装 std::array 以创建循环数组,但如何提供聚合初始化

c++ - OpenCV 中的嘈杂色调

c# - 在多语言应用程序中使用的类库中表示性别的最佳方式

C++ 单例/事件对象范例

c - 预处理器数据检索宏

c++ - 在安装过程中如何设置对MSI中当前登录用户(或任何其他本地用户)的文件夹共享访问权限?

c++ - 读取二进制文件c++

javascript - 为什么我在这个例子中得到 "Cannot read property ' text' of undefined"?

c - 为什么C语言给字符数组赋值时没有 '&'?

c - 指针地址位置