【问题标题】:Polymorphic function in class which is implementing abstract class实现抽象类的类中的多态函数
【发布时间】:2015-04-17 05:00:35
【问题描述】:
class Eq
{
public:
    virtual bool operator==(Eq operand) = 0;
};

class Day : public Eq
{
public:
    enum DAY
    {
        Monday,
        Tuesday,
        Wednesday,
        Thursday,
        Friday,
        Saturday,
        Sunday
    } day;
    Day(DAY init_day) : day(init_day){}
    bool operator==(Day operand)
    {
        return day == operand.day;
    }
};

int main()
{
    Day d1(Day::Monday);
    Day d2(Day::Monday);
    d1 == d2;
    return 0;
}

这是我试图实现的代码。 Eq 类就像 JAVA 的接口一样工作。它只有一个成员函数,operator==。 Operator== 采用一个与派生 Eq 类的类相同类型的操作数。例如Day类派生Eq,所以必须实现operator==,取Day类型参数。

还有问题。首先,两个 operator== 函数都有不同的签名。所以编译器认为它们不是相同的函数,而是被覆盖的函数。其次,如果此代码中的任何函数接受 Eq 类参数,则编译器会出错,因为抽象类的实例不可用。

这就是问题所在。如何使 T 类中的 operator== 函数获取 T 类参数,其中 T 是 Eq 类的实现?

【问题讨论】:

  • 这是您想要实现的目标,还是一个示例?我的意思是,您不需要将operator== 设为纯虚拟来强制用户实现自己的operator==。由于没有默认的operator==,如果Day 没有实现operator==d1 == d2 将无法编译。
  • 你应该真正学会通过引用传递参数,而不是通过值。
  • 我同意@KirilKirov 如果这就是你所做的一切,不要在这里为基类烦恼。还要使操作符成为 const 函数。

标签: c++ abstract-class


【解决方案1】:

您可以在Eq 中引入类型参数T 并从Eq<Day> 派生Day 以强制执行所需的operator== 签名。

【讨论】:

    【解决方案2】:

    你可以使用 CRTP:

    template <typename DerivedType>
    class Eq
    {
    public:
        virtual bool operator==(DerivedType operand) = 0;
    };
    
    class Day : public Eq<Day>
    {
    public:
        enum DAY
        {
            Monday,
            Tuesday,
            Wednesday,
            Thursday,
            Friday,
            Saturday,
            Sunday
        } day;
        Day(DAY init_day) : day(init_day){}
        bool operator==(Day operand)
        {
            return day == operand.day;
        }
    };
    

    【讨论】:

    • 在这一点上,使函数虚拟化并没有太大意义。
    • 你可能不想在这里按值传递参数。
    • @Dan:你是对的,但是如果想从 Day 派生一些东西并尝试覆盖 ==。无论如何,我试图尽可能少地修改以使其工作:)
    【解决方案3】:

    您可以通过发送参考来做到这一点。

    class Eq
    {
    public:
        virtual bool operator==(Eq& operand) = 0; // Changed to reference
    };
    
    class Day : public Eq
    {
    public:
        enum DAY
        {
            Monday,
            Tuesday,
            Wednesday,
            Thursday,
            Friday,
            Saturday,
            Sunday
        } day;
        Day(DAY init_day) : day(init_day){}
        bool operator==(Eq& operand) // Changed to reference of Eq.
        {
            const Day* other = dynamic_cast<Day*> (&operand);
            if (other == nullptr) // Checking if the operand is of Day type.
                return false; // If not, those two objects cannot be the same.
            return day == other->day; // Do the comparison. 
            // Note the '->' instead of '.' since 'day' is a pointer.
        }
    };
    

    与 Java 相比,您缺少 C++ 工作原理的几个关键点(我猜您对 Java 更熟悉)。

    1. 要进行虚拟覆盖,您必须具有相同的方法签名。
    2. 为避免切片,您需要发送引用或指针,而不是堆栈上的对象。
    3. 最后,发送值可能会占用大量内存(与引用或指针相比)。

    以下是我找到的几个很好的链接,这些链接是关于在处理继承时如何进行 operator== 重载的。 (他们还谈到了 public-non-virtual/non-public-virtual 的讨论。)

    Equality Test for Derived Classes in C++

    What's the right way to overload operator== for a class hierarchy?

    【讨论】:

    • 这个答案是 OP 可能想要的(即 Java 所做的)。
    【解决方案4】:

    您的方法有两个主要缺陷:

    • Dayoperator==签名Eq 的签名不匹配,因此它不起作用。
    • 您按值传递运算符的操作数。这意味着将有slicing(即传递的对象可能会丢失所有不在 Eq 中的成员,这就是这一天)

    已经有很好的模板解决方案提出了,对于这类问题非常方便。为了完整起见,我建议您使用无模板的替代方案。

    第一次机会Eq

    class Eq
    {
    public:
        virtual bool operator==(Eq& operand) = 0;  // References passing doesn't slice
    };
    

    然后在Day 中更新其覆盖:

    bool operator==(Eq& operand)
    {
        if (dynamic_cast<Day*>(&operand))  // check if types compatible 
            return day == dynamic_cast<Day*>(&operand)->day;
        else return false;  // else the objects are different anyway
    }
    

    终于可以在main()测试了:

     ...
    if (d1 == d2)  // check that result is as expected !
        cout << "Yes it works !";  
    else cout << "Hhmmm..."; 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-18
      • 1970-01-01
      • 1970-01-01
      • 2021-05-12
      • 2016-03-12
      相关资源
      最近更新 更多