修改
主要思想是创建一个允许添加新类型元素的界面。例如,添加字符串,使用“X”算法计算其距离。这就是我认为模板不能成为正确答案的原因。
我在Element类中更改了函数的距离定义,现在它已经实现了。
class Element : public Object
{
...
virtual float distance(Element *other){return INFINITE;};
...
}
对于Vector类,它的变化相同:
class Vector : public Element
{
...
virtual float distance(Element *other);
...
}
其实施是:
float Vector::distance(Element *other)
{
if(other->getClass() != this->getClass()) return INFINITE;//this came from Object class
Vector *n_other = dynamic_cast<Vector*>(other);
float result = this->L2D(*this,*n_other);
return result;
}
OLD
我正在尝试在C ++中使用接口,以便子类可以编写这些方法。
在这种情况下,距离表示一个值,表示两个元素的接近程度(相似)。
例如我现在正在尝试使用向量,但将来我会使用字符串或其他东西,如文档,面等等......它可以像元素一样使用。
//Element.h
class Element
{
virtual float distance(Element const&, Element const&)=0;
};`
然后我有了类Vector
//Vector.h
#include "Element.h"
class Vector: public Element
{
float distance(Vector const&, Vector const&);//(?)
};
实施
#include "Vector.h"
float Vector::distance(Element const &a, Element const &b)
{
return Vector::L2D(a,b);//Euclidean distance
}
我的问题是我怎么能这样做,因为我找不到这个问题的例子。 我不确定你是否能理解我想要做的事情......我希望如此。 谢谢大家。
答案 0 :(得分:2)
您要求的内容称为argument covariance。也就是说,重写方法的参数随着继承的改变而改变。这在直觉上是有道理的,但可以是案例类型的安全问题。让我们假设它是可能的,并编译你的代码。那会发生什么呢?
Element* e = new Vector();
e->distance(Element(), Element()); // but Vector::distance expects an Element!
这种覆盖形式在我所知道的所有传统语言中都是非法的。为了使覆盖有效,您无法更改参数&#39;类型(有时您可能会更改返回类型)。
因此,您的选择是:
请注意,使用模板无法帮助您 - 虚拟功能不能成为模板。
答案 1 :(得分:1)
我从基类Element
中看到的是,这是一个抽象类型,因为它的至少一个方法被声明为purely virtual
。这意味着两件事:首先,您不能创建类型为Element
的对象,其次,所有继承的类必须实现任何声明为pure virtual
的函数。
在您的基类中,您将其作为声明:
virtual float distance(Element const&, Element const&)=0;
并且在派生类中,您将此作为声明:
float distance(Vector const&, Vector const&);//(?)
在派生类的实现中你有这个:
float Vector::distance(Element const &a, Element const &b) {
return Vector::L2D(a,b);//Euclidean distance
}
基类声明所有派生类必须实现的纯虚函数必须返回一个float,并且必须接受对Element类型的两个const引用。但是在你继承的类中,它的声明是另外说明的:它声明这个函数确实返回一个不是问题的浮点数,但是它的参数类型是因为它被声明为对Vector类型进行两次const引用。如果您使用的是MS Visual Studio 2012或更高版本,您可以尝试这样做:在继承的类中的函数声明之后添加关键字override
,以便您继承的声明如下所示:
float distance(Vector const&, Vector const&) override;
然后尝试编译并查看是否有任何编译器,构建或链接错误。
下一步是更改派生类的声明和定义,以匹配基类的纯虚拟签名声明。执行此操作后,派生函数声明将如下所示:
float distance(Element const&, Element const&) override;
要了解这里发生了什么;这是我对原始代码所做的一个示例,因此您可以看到正在使用抽象类型和继承的内容。
<强> element.h展开强>
#ifndef ELEMENT_H
#define ELEMENT_H
class Element {
public:
virtual float distance( Element const&, Element const& ) = 0;
}; // Element
#endif // ELEMENT_H
<强> Element.cpp 强>
#include "stdafx.h"
#include "Element.h"
// ----------------------------------------------------------------------------
// distance()
float Element::distance( Element const&, Element const& ) {
return 0;
} // distance
<强> Vector.h 强>
#ifndef VECTOR_H
#define VECTOR_H
#include "Element.h"
class Vector : public Element {
public:
float distance( Element const&, Element const& ) override;
}; // Vector
#endif // VECTOR_H
<强> Vector.cpp 强>
#include "stdafx.h"
#include "Vector.h"
// ----------------------------------------------------------------------------
// distance()
float Vector::distance( Element const&, Element const& ) {
return 1;
} // distance
<强>的main.cpp 强>
#include "stdafx.h"
#include "Vector.h"
int main() {
float val = 0;
Vector v1;
Vector v2;
Vector v3;
val = v3.distance( v1, v2 );
std::cout << val << std::endl;
std::cout << "Press any key to quit" << std::endl;
_getch();
return 0;
} // main
很难确切地说出你在问什么以及你想要实现什么,但是从单独查看你的代码来看,你似乎正在使用继承的抽象类型,但你继承的成员函数签名是错误的,因为它与基数的纯虚方法签名不匹配。
如果仔细观察我提交给你的小程序;这个编译,构建和执行,我得到了适当的结果。
如果您注意到这里,声明期望两个const&
到一个Element
对象的类型,但是当我调用该方法时,我传入两个声明的变量类型或{{{的实例1}}对象。这是有效的,因为Vector
对象是从Vector
类型继承的。另外,如果查看输出值,则打印的值为1而不是0.当您尝试Element
基类的函数声明时,这就是您想要的。
override
返回0 - 无法调用它,因为它是纯虚方法,导致基类为抽象
Element::distance()
返回1 - 这将在矢量对象上调用。
也许这会帮助你实现目标。
答案 2 :(得分:0)
当子类方法应该具有不同的签名时,我不确定接口的用途是什么。可能的是:
#include <iostream>
template <typename T>
struct DistInterf {
virtual double distance(T other)=0;
};
struct A : DistInterf<A> {
double distance(A other){
return 2;
}
};
int main() {
A t,t2;
std::cout << t.distance(t2) << std::endl;
return 0;
}
但是,我没有看到在这里使用界面的任何好处,因为每个子类将实现不同的接口(因为我理解这是你要求的问题)。
我在评论中已经提到过:距离函数应该是静态的,或者只需要一个参数,否则它就没什么意义了。
PS:我能想到一个用例:template<typename T>
void foo(T t1,T t2){
std::cout << t1.distance(t2) << std::endl;
}
您可以将实现上述interace的任何类型作为模板参数传递。