我创建了这个.h文件
#pragma once
namespace Core
{
class IComparableObject
{
public:
virtual int CompareTo(IComparableObject obj)=0;
};
}
Run Code Online (Sandbox Code Playgroud)
但是如果方法是虚拟的,编译器不喜欢IComparableObject obj param
virtual int CompareTo(IComparableObject obj) {}
Run Code Online (Sandbox Code Playgroud)
没关系,但是我希望它是虚拟的.我怎么能设法做到这一点?可能吗?
den*_*ane 27
你试图obj通过价值传递.您不能按值传递抽象类实例,因为不能直接实例化抽象类(直接).要做你想做的事,你必须obj通过引用传递,例如:
virtual int CompareTo(IComparableObject const &obj)=0;
Run Code Online (Sandbox Code Playgroud)
当你给出一个实现时它会起作用,CompareTo因为那时类不再是抽象的了.但请注意切片发生!你不想传递obj价值.
那么我必须在这里给出一个意想不到的答案!Dennycrane说你可以这样做:
virtual int CompareTo(IComparableObject const &obj)=0;
但这也不正确.哦,是的,它编译,但它没用,因为它永远无法正确实现.
此问题是(静态类型)对象方向崩溃的基础,因此使用OO的程序员识别问题至关重要.这个问题有一个名字,它被称为协方差问题,它完全摧毁了OO作为一般的编程范式; 也就是说,一种表示和独立实现一般抽象的方法.
这个解释会有点长而且草率,所以请耐心等待,并试着在两行之间进行阅读.
首先,可以在任何派生类中轻松实现具有不带参数的纯虚方法的抽象类,因为该方法可以通过this指针访问派生类的非静态数据变量.this指针具有指向派生类的指针类型,因此我们可以说它随类而变化,实际上它与派生类类型是协变的.
让我把这种多态性称为第一顺序,它显然支持在对象类型上调度谓词.实际上,这种方法的返回类型也可能随着对象和类类型的变化而变化,也就是说,返回类型是协变的.
现在,我将概括一个没有参数的方法的概念,允许任意标量参数(例如int)声明这个改变没有:这只是一个由标量类型索引的方法系列.这里的重要属性是标量类型是封闭的.在派生类中,必须使用完全相同的标量类型.换句话说,类型是不变的.
对虚函数的不变参数的一般介绍仍然允许多态,但结果仍然是第一顺序.
不幸的是,这些函数的实用性有限,尽管它们在抽象只是第一顺序时非常有用:一个很好的例子就是设备驱动程序.
但是,如果你想模拟一些实际上有趣的东西,也就是说它至少是一种关系呢?
答案是:你不能这样做.这是一个数学事实,与所涉及的编程语言无关.让我们假设你有一个抽象的说法,数字,你想要一个数字添加到另一个数字,或比较它们(如在OP的例子中).忽略对称性,如果您有N个实现,则必须编写N ^ 2个函数来执行操作.如果添加抽象的新实现,则必须编写N + 1个新函数.
现在,我有第一个证明OO被搞砸的证据:你不能将N ^ 2方法放入虚拟调度模式中,因为这样的模式是线性的.N个类为您提供了N个方法可以实现,N> 1,N ^ 2> N,因此OO被拧紧,QED.
在C++上下文中,您可以看到问题:考虑:
struct MyComparable : IComparableObject {
int CompareTo(IComparableObject &other) { .. }
};
Arggg!我们搞砸了!我们不能在 .. 这里填写部分,因为我们只引用了一个抽象,它没有数据可供比较.当然必须如此,因为存在开放/不确定/无限数量的可能实现.没有办法将单个比较例程写成公理.
当然,如果你有各种属性例程,或者你可以做一个通用的通用表示,但这不算数,因为那时映射到通用表示是无参数的,因此抽象只是第一顺序.例如,如果你有各种整数表示,并通过将它们都转换为GNU gmp的数据类型mpz来添加它们,那么你使用两个协变投影函数和一个全局非多态比较或加法函数.
这不是反例,它是问题的非解决方案,其表示在至少两个变量(至少是自身和其他)中协变的关系或方法.
你可能认为你可以解决这个问题:
struct MyComparable : IComparableObject {
int CompareTo(MyComparable &other) { .. }
};
毕竟你可以实现这个接口,因为你现在知道其他的表示,因为它是MyComparable.
不要嘲笑这个解决方案,因为它正是Bertrand Meyer在Eiffel中所做的,而且很多人在C++中做了很少的改动,尝试解决它不是类型安全且实际上没有覆盖的事实基类功能:
struct MyComparable : IComparableObject {
int CompareTo(IComparableObject &other) {
try
MyComparable &sibling = dynamic_cast(other);
...
catch (..) { return 0; }
}
};
这不是解决方案.它说两件事并不相同,只是因为它们有不同的表现形式.这不符合要求,即在抽象中比较两件事.两个数字,例如,不能不等于仅仅因为使用的表示是不同的:零等于零,即使一个是MPZ和其他一个int.请记住:这个想法是正确地表示抽象,这意味着行为必须仅依赖于抽象值,而不是特定实现的细节.
有些人尝试过双重调度.显然,这也无济于事.这里没有可能摆脱基本问题:你不能把一个正方形填入一条线.虚函数调度是线性的,二阶问题是二次的,所以OO不能代表二阶问题.
现在我想非常清楚在这里,C++和其他静态类型的面向对象的语言被打破,不是因为他们不能解决这个问题,因为它不能得到解决,这是不是一个问题:它的一个简单的事实.这些语言和OO范式一般被打破的原因是因为它们承诺提供一般的抽象,然后不这样做.在C++的情况下,这是承诺:
struct IComparableObject { virtual int CompareTo(IComparableObject obj)=0; };
这里是隐含合约被破坏的地方:
struct MyComparable : IComparableObject {
int CompareTo(IComparableObject &other) { throw 00; }
};
因为我给出的实施实际上是唯一可行的.
离开之前,您可能会问:什么是正确的方式(TM).答案是:使用函数式编程.在C++中,这意味着模板.
template<class T, class U> int compare(T,U);
Run Code Online (Sandbox Code Playgroud)
因此,如果您要比较N种类型,并且实际上比较了所有组合,那么确实您必须提供N ^ 2个特化.这表明模板实现了承诺,至少在这方面.这是一个事实:如果函数在多个参数中是变体,则无法在运行时在一组开放类型上进行调度.
BTW:如果你没有被说服理论..只是去看看ISO C++标准库,看看有多少虚函数的多态性目前使用的是,相较于模板函数式编程..
最后,请仔细注意,我不是说类和这样等没用的,我用虚函数的多态性自己:我是说,这是仅限于特定的问题,而不是代表抽象的一般方式,因此不值得被称为范式.