在方法中使用自身的接口
Interfaces which use itself in methods
编辑
主要想法是制作一个允许添加新类型元素的界面。例如添加字符串,使用 'X' 算法计算其距离。这就是为什么我认为模板不是正确答案的原因。
我已经在 Element class 中更改了函数的距离定义,现在它有一个实现。
class Element : public Object
{
...
virtual float distance(Element *other){return INFINITE;};
...
}
对于 Vector class 其相同的更改:
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;
}
旧
好吧,我正在尝试用 C++ 做一个接口,以便 child classes 可以编写这些方法。
在这种情况下,距离表示一个值,表示两个元素有多接近(相似)。
例如,我现在正在尝试使用矢量,但将来我会使用字符串或其他东西,如文档、面孔等...它可以像元素一样使用。
//Element.h
class Element
{
virtual float distance(Element const&, Element const&)=0;
};`
然后我有 class 向量
//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
}
我的问题是我该怎么做,因为我找不到这个问题的例子。
我不太确定您是否能理解我正在尝试做的事情……但愿如此。
谢谢大家
您要的是argument covariance。即,让覆盖方法的参数随着继承的变化而变化。这在直觉上是有道理的,但可能会导致类型安全问题。让我们假设这是可能的,并且您的代码已编译。那这里会发生什么呢?
Element* e = new Vector();
e->distance(Element(), Element()); // but Vector::distance expects an Element!
这种覆盖形式在我所知道的所有传统语言上都是非法的。要使覆盖有效,您不能更改参数的类型(不过有时您可以更改 return 类型)。
因此您的选择是:
- 坚持基本签名,并使用 dynamic_cast 来(尝试)向下转换覆盖方法的参数
- 避免使用纯虚函数,让每个 class 定义自己的方法,并使用匹配的参数类型。
- 在这种情况下,double-dispatch 等模式有时会派上用场。
请注意,使用模板在这里对您没有帮助 - 虚函数不能是模板。
当 child classes 方法应该具有不同的签名时,我不太确定接口的用途是什么。可能是这样的:
#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;
}
但是,我看不出在此处使用接口有任何优势,因为每个 child class 将实现不同的接口(据我了解,这就是您要问的问题)。
我已经在评论中提到:距离函数应该是静态的或者只接受一个参数,否则恕我直言。
PS:我能想到一个用例:
template<typename T>
void foo(T t1,T t2){
std::cout << t1.distance(t2) << std::endl;
}
您可以传递任何实现上述接口的类型作为模板参数。
我从你的基础 class Element
可以看出这是一个抽象类型,因为它的至少一个方法被声明为 purely virtual
。这意味着两件事:首先,您不能创建 Element
类型的对象,其次,所有继承的 classes 必须实现任何声明为 pure virtual
的函数。
在你的基础 class 中你有这样的声明:
virtual float distance(Element const&, Element const&)=0;
并且在您的派生 class 中,您将其作为声明:
float distance(Vector const&, Vector const&);//(?)
并且在您派生的 class 的实现中您有这个:
float Vector::distance(Element const &a, Element const &b) {
return Vector::L2D(a,b);//Euclidean distance
}
基础 class 声明所有派生 class 必须实现的纯虚函数必须 return 一个浮点数,并且必须接受两个对元素类型的常量引用。但是,在您继承的 class 中,它的声明另有说明:它声明该函数执行 return 一个浮点数,这不是问题,但它的参数类型是因为它被声明为采用两个 const 引用到 Vector 类型。如果您使用的是 MS Visual Studio 2012 或更新版本,您可以尝试这样做:在继承的 class 中的函数声明之后添加关键字 override
以便您的继承声明如下所示:
float distance(Vector const&, Vector const&) override;
然后尝试编译,看看是否有任何编译器、构建或 link 错误。
下一步是更改派生 class 的声明和定义,以匹配基础 class 的纯虚拟签名声明。执行此操作后,您的派生函数声明将如下所示:
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
对象的类型,但是当我调用该方法时,我传递了两个声明的变量类型或一个实例Vector
对象。这是有效的,因为 Vector
对象是从 Element
类型继承的。此外,如果您查看输出值,打印的值是 1 而不是 0。这就是您尝试 override
基础 class 的函数声明时想要的。
Element::distance()
returns 0 - 这不能被调用,因为它是一个纯虚拟方法,导致基础 class 是抽象的
Vector::distance()
returns 1 - 这将在矢量对象上调用。
也许这会帮助您实现目标。
编辑
主要想法是制作一个允许添加新类型元素的界面。例如添加字符串,使用 'X' 算法计算其距离。这就是为什么我认为模板不是正确答案的原因。
我已经在 Element class 中更改了函数的距离定义,现在它有一个实现。
class Element : public Object
{
...
virtual float distance(Element *other){return INFINITE;};
...
}
对于 Vector class 其相同的更改:
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;
}
旧
好吧,我正在尝试用 C++ 做一个接口,以便 child classes 可以编写这些方法。
在这种情况下,距离表示一个值,表示两个元素有多接近(相似)。
例如,我现在正在尝试使用矢量,但将来我会使用字符串或其他东西,如文档、面孔等...它可以像元素一样使用。
//Element.h
class Element
{
virtual float distance(Element const&, Element const&)=0;
};`
然后我有 class 向量
//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
}
我的问题是我该怎么做,因为我找不到这个问题的例子。 我不太确定您是否能理解我正在尝试做的事情……但愿如此。 谢谢大家
您要的是argument covariance。即,让覆盖方法的参数随着继承的变化而变化。这在直觉上是有道理的,但可能会导致类型安全问题。让我们假设这是可能的,并且您的代码已编译。那这里会发生什么呢?
Element* e = new Vector();
e->distance(Element(), Element()); // but Vector::distance expects an Element!
这种覆盖形式在我所知道的所有传统语言上都是非法的。要使覆盖有效,您不能更改参数的类型(不过有时您可以更改 return 类型)。
因此您的选择是:
- 坚持基本签名,并使用 dynamic_cast 来(尝试)向下转换覆盖方法的参数
- 避免使用纯虚函数,让每个 class 定义自己的方法,并使用匹配的参数类型。
- 在这种情况下,double-dispatch 等模式有时会派上用场。
请注意,使用模板在这里对您没有帮助 - 虚函数不能是模板。
当 child classes 方法应该具有不同的签名时,我不太确定接口的用途是什么。可能是这样的:
#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;
}
但是,我看不出在此处使用接口有任何优势,因为每个 child class 将实现不同的接口(据我了解,这就是您要问的问题)。
我已经在评论中提到:距离函数应该是静态的或者只接受一个参数,否则恕我直言。
PS:我能想到一个用例:
template<typename T>
void foo(T t1,T t2){
std::cout << t1.distance(t2) << std::endl;
}
您可以传递任何实现上述接口的类型作为模板参数。
我从你的基础 class Element
可以看出这是一个抽象类型,因为它的至少一个方法被声明为 purely virtual
。这意味着两件事:首先,您不能创建 Element
类型的对象,其次,所有继承的 classes 必须实现任何声明为 pure virtual
的函数。
在你的基础 class 中你有这样的声明:
virtual float distance(Element const&, Element const&)=0;
并且在您的派生 class 中,您将其作为声明:
float distance(Vector const&, Vector const&);//(?)
并且在您派生的 class 的实现中您有这个:
float Vector::distance(Element const &a, Element const &b) {
return Vector::L2D(a,b);//Euclidean distance
}
基础 class 声明所有派生 class 必须实现的纯虚函数必须 return 一个浮点数,并且必须接受两个对元素类型的常量引用。但是,在您继承的 class 中,它的声明另有说明:它声明该函数执行 return 一个浮点数,这不是问题,但它的参数类型是因为它被声明为采用两个 const 引用到 Vector 类型。如果您使用的是 MS Visual Studio 2012 或更新版本,您可以尝试这样做:在继承的 class 中的函数声明之后添加关键字 override
以便您的继承声明如下所示:
float distance(Vector const&, Vector const&) override;
然后尝试编译,看看是否有任何编译器、构建或 link 错误。
下一步是更改派生 class 的声明和定义,以匹配基础 class 的纯虚拟签名声明。执行此操作后,您的派生函数声明将如下所示:
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
对象的类型,但是当我调用该方法时,我传递了两个声明的变量类型或一个实例Vector
对象。这是有效的,因为 Vector
对象是从 Element
类型继承的。此外,如果您查看输出值,打印的值是 1 而不是 0。这就是您尝试 override
基础 class 的函数声明时想要的。
Element::distance()
returns 0 - 这不能被调用,因为它是一个纯虚拟方法,导致基础 class 是抽象的
Vector::distance()
returns 1 - 这将在矢量对象上调用。
也许这会帮助您实现目标。