【问题标题】:c++ forcing operator == overload from interface abstract classc ++强制运算符==从接口抽象类重载
【发布时间】:2013-01-01 15:28:13
【问题描述】:

在 c++ 上使用接口(抽象类)我需要强制任何类而不是继承接口来实现运算符 ==。 考虑这种情况:

class IBase
{
   virtual void someFunc() const = 0; 
}

class CInheritClass : public IBase
{
   virtual void someFunc() const; 
   virtual bool operator== ( const CInheritClass& obj ) const;
}

void main()
{
  CInheritClass instance;
}

类 CInheritClass 必须实现 someFunc,因为它继承了 Ibase,实现 virtual bool operator== ( const CInheritClass& obj ) const; 不是强制性的。 我想以任何继承者 X 都必须实现的方式修改 IBase 类

virtual bool operator== ( const X& obj ) const

以下代码将起作用:

template<class X>
class IBase
{
   virtual void someFunc() const = 0; 
   virtual bool operator== ( const X& obj ) const = 0;

}

class CInheritClass : public IBase<CInheritClass>
{
   virtual void someFunc() const; 
   virtual bool operator== ( const CInheritClass& obj ) const;
}

但我追求不使用模板的解决方案,导致每个希望实现 IBase 的类都必须继承 IBase 并将其自身作为模板类class X : public IBase&lt;X&gt;,这对于任何可能查看的未来开发人员来说都是令人困惑和不清楚的我的代码。 有什么想法吗?

【问题讨论】:

  • 为什么?我是认真的——你想要这个的原因会改变你得到的答案。你打算如何使用operator== 之类的类?
  • 您正在定义一系列具有不同参数类型的虚函数......这些不适用于多态性。
  • 您建议的模板方法相当普遍,因此只要准确反映您的设计目标,我就不会将其归类为“令人困惑和不清楚”。不过,我不确定为什么在您的示例中您没有将基类版本设为纯虚拟。
  • 欧文,我错误地没有把它变成纯虚拟的。修复。模板方法有几个缺点。其中包括 Ben 建议的多态性问题。 Yakk - 我需要这个实现的原因是因为类 CinheritClass 最终是第三类的一部分,它的实现需要运算符重载
  • 你的解决方案其实很合理,一点也不混乱。很多开发者都熟悉CRTP,不熟悉的开发者应该学习一下。 .NET 语言现在使用 CRTP 来指定例如IComparable 界面,方式与您所做的非常相似。

标签: c++ operator-overloading abstract-class


【解决方案1】:

使用纯虚函数声明

virtual bool operator== ( const IBase& obj ) const = 0;

实现示例:

bool CInheritClass ::operator==( const IBase& obj ) const
{
     const CInheritClass *o = dynamic_cast<const CInheritClass*>(&obj);
     if (o == NULL) return false;
     // TODO
}

输入类型应该是基类,否则你将无法对基类进行多态调用比较

对于从模板继承的想法:警告,2个派生类之间的基类不再相同。 =>基类的目的改变了:使用模板你只是共享代码,强制用户实现功能并防止2个派生类之间重复代码

这真的取决于你想要做什么。它可以是一个好的设计,也可以不是......

【讨论】:

  • 他想让每个子类与它自己的类型相比,而不是一个特定的子类。
  • 显然 OP 已经知道纯虚拟声明,因为他在问题中使用了一个。
  • 现在您正在使每个子类与 所有 其他子类具有可比性。
  • @benjarobin 不一定。诸如apple == car(假设合适的类型)之类的比较通常(但不总是)会产生编译时错误,因为它几乎肯定不会是程序员想要的,而您的方法会让它编译但总是返回false.
  • @Oded 请注意,此答案在运行时而不是编译时检查类型错误,并为每个 operator== 调用添加字符串比较开销(dyanmic_cast 的成本基本上归结为装饰字符串在某些情况下进行比较)。
【解决方案2】:

仅供参考,模板版本通常称为“Barton-Nackman 技巧”: http://en.wikipedia.org/wiki/Barton%E2%80%93Nackman_trick

如果您希望您的派生类只能与它们自己类型(而不是抽象基类型)的其他对象进行比较,那么这就是使用的“技巧”,IMO。

编辑:这类似于 Java 实现 Comparable 接口的方式,因此(好)程序员不会不知道发生了什么: http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html

【讨论】:

    【解决方案3】:

    让我们在这里分解您的要求。您想强制每个孩子使用其自己类型的右侧来实现 operator==。但是您还要求将所述操作员设为虚拟。由于每个子运算符的参数类型将与父虚拟方法的参数类型不同,因此您最终会隐藏基类运算符。因此,您将永远无法以多态方式调用它,并且比较的虚拟性根本没有用处。

    相反,我的建议是删除父运算符的虚拟性,并在使用子类时使用 ==。然后编译器会告诉你它什么时候丢失。

    但是,如果您确实需要能够多态地调用基础对象上的比较,您很可能必须使用双重调度之类的东西来确保双方都访问最衍生的类型并验证最衍生的类型相同(否则比较无意义)。

    编辑:如果您确实需要该功能,我看不出模板基类有什么特别的问题 - 只需检查您的设计并确保您确实需要它。

    【讨论】:

    • 我明白你的意思。您对虚拟性部分是正确的。我可以删除它。并从我的基类中删除运算符 == 并在使用未实现 == 的类时依靠编译器来查找错误。它会起作用,它确实起作用。但我正在寻求完美,并且界面确实需要重载 == 运算符。在您的解决方案中,您要求我使用未列出所有抽象函数的接口,并且一旦某个类无法实现未列出的函数之一,则会在其他地方引发错误。它将起作用。但这是不好的做法。
    • @Oded No Mark 关于虚拟部分不正确。首先拥有接口的主要(我实际上认为这是唯一目的)目的是定义行为 - 如果您从接口中删除 == 运算符,这意味着相等检查不再是“行为契约”的一部分.如果您更改界面以进行微级别/语言功能级别的调整,通常会导致糟糕的设计;在大型项目中,灾难。
    猜你喜欢
    • 2015-05-09
    • 2012-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-13
    • 1970-01-01
    • 2017-11-27
    相关资源
    最近更新 更多