C++ 以抽象 class 作为参数重写虚方法
C++ override virtual method with abstract class as a parameter
我有以下摘要class
class A {
public:
virtual void foo(A* a) = 0;
}
和几个 class 继承自此 class。例如
class B : public A {
public:
void foo(A* a); // implementation in a separete file
}
但是,我只想 class B
接受自己作为 foo
中的参数
void foo(B* b);
是否可以在 C++ 中执行此操作?
我考虑过一个模板,但语法允许太多的灵活性。可以写 class B: public A<B>
,但我想要 class B: public A<C>
.
的编译器错误
--编辑--
看来我使用抽象class 是不合理的。让我澄清一下我的情况。
我在单独的函数中使用 A
的多态行为。除此之外,我想定义一个函数,它接收与上述相同类型的参数。我正在尝试编写一个函数来定义派生 class 的两个对象之间的距离。距离仅在来自相同 class 的对象(b1
和 b2
,或 c1
和 c2
,而不是 b1
和 c2
).我也想尽可能以一般方式访问此距离函数。
--编辑2--
Cássio 展示了为什么无法执行基于编译器的检查。 zar 的解决方案通过运行时错误检查为代码添加了更多结构。
这不是 virtual
的目的。
virtual
用于启用多态行为。基本上,要启用它:
struct A {virtual void foo()=0;};
// Two different "behaviors" for the same "A"
struct B {void foo() override{}};
struct C {void foo() override{}};
// forgive the leak, this is just to prove a point.
A* b = new B();
A* c = new C();
b->foo(); // Will call B::foo, even though this is a pointer to "A"
c->foo(); // Will call C::foo, even though this is a pointer to "A"
你尝试使用它的方式,你失去了这个好处,你只是白白得到了虚函数的性能损失。实例化未实现某些纯虚函数的 class 是错误的事实仅仅是为了防止格式错误的程序。
如果您想确保 B
实现了某个接口,只需在某处使用该接口即可。如果 B
没有实现它,您将得到您正在寻找的编译器错误:
class B {};
template<typename T> void call_foo(T* v1, T* v2) {
v1->foo(&v2);
}
B b1;
B b2;
b1.foo(&b2); // error
call_foo(&b1, &b2); // error
然后,要消除错误,您只需实现该功能即可。不需要 virtual
:
class B {
void foo(B*) {/*do something*/}
};
B b1;
B b2;
b1.foo(&b2); // ok
call_foo(&b1, &b2); // ok
但是,为什么我不能为此使用虚函数?
想象一下以下场景:
struct A {virtual void foo(A*)=0;};
// Imagine if the language allowed this:
struct B {void foo(B*) override{}};
struct C {void foo(C*) override{}};
// (...)
// I create a vector of objects, and insert three of them in this vector.
std::vector<A*> objects;
// Note that foo is well-defined only for the first two.
objects.push_back(new B();)
objects.push_back(new B();)
objects.push_back(new C();)
// Then I shuffle the vector
std::shuffle(objects.begin(), objects.end());
// At least one of these three lines should give a compiler error.
// Which one(s)?
objects[0]->foo(objects[1]);
objects[0]->foo(objects[2]);
objects[1]->foo(objects[2]);
但是我需要函数是虚的,我需要类型安全!
虚函数是一种运行时机制。您必须在运行时检查类型。 已经很好地涵盖了这一点,所以我不会深入细节。总结一下:简单地dynamic_cast
转换成你想要的类型,如果转换成returnsnullptr
,你的类型就错了。然后您可以抛出异常或打印一些诊断消息。
我了解到您的问题更多是关于语法的。你有什么是对的,只要传递一个B类型的对象。定义仍然会说A但是它会很乐意接受派生class。您不需要对此进行任何特殊定义。
class A {
public:
virtual void foo(A* a) = 0;
};
class B : public A {
public:
void foo(A* a)
{
if (dynamic_cast<B*> (a) == NULL)
std::cout << "wrong type, expecting type B\r\n";
}
};
class C : public A {
public:
void foo(A* a)
{
if (dynamic_cast<C*> (a) == NULL)
std::cout << "wrong type, expecting type C\r\n";
}
};
int main()
{
B * b1 = new B;
B * b2 = new B;
C * c1 = new C;
C * c2 = new C;
b2->foo(c1); // bad
c1->foo(b1); // bad
b2->foo(b1); // good
delete b1;
delete b2;
delete c1;
delete c2;
}
另见 dynamic_cast。
我有以下摘要class
class A {
public:
virtual void foo(A* a) = 0;
}
和几个 class 继承自此 class。例如
class B : public A {
public:
void foo(A* a); // implementation in a separete file
}
但是,我只想 class B
接受自己作为 foo
void foo(B* b);
是否可以在 C++ 中执行此操作?
我考虑过一个模板,但语法允许太多的灵活性。可以写 class B: public A<B>
,但我想要 class B: public A<C>
.
--编辑--
看来我使用抽象class 是不合理的。让我澄清一下我的情况。
我在单独的函数中使用 A
的多态行为。除此之外,我想定义一个函数,它接收与上述相同类型的参数。我正在尝试编写一个函数来定义派生 class 的两个对象之间的距离。距离仅在来自相同 class 的对象(b1
和 b2
,或 c1
和 c2
,而不是 b1
和 c2
).我也想尽可能以一般方式访问此距离函数。
--编辑2--
Cássio 展示了为什么无法执行基于编译器的检查。 zar 的解决方案通过运行时错误检查为代码添加了更多结构。
这不是 virtual
的目的。
virtual
用于启用多态行为。基本上,要启用它:
struct A {virtual void foo()=0;};
// Two different "behaviors" for the same "A"
struct B {void foo() override{}};
struct C {void foo() override{}};
// forgive the leak, this is just to prove a point.
A* b = new B();
A* c = new C();
b->foo(); // Will call B::foo, even though this is a pointer to "A"
c->foo(); // Will call C::foo, even though this is a pointer to "A"
你尝试使用它的方式,你失去了这个好处,你只是白白得到了虚函数的性能损失。实例化未实现某些纯虚函数的 class 是错误的事实仅仅是为了防止格式错误的程序。
如果您想确保 B
实现了某个接口,只需在某处使用该接口即可。如果 B
没有实现它,您将得到您正在寻找的编译器错误:
class B {};
template<typename T> void call_foo(T* v1, T* v2) {
v1->foo(&v2);
}
B b1;
B b2;
b1.foo(&b2); // error
call_foo(&b1, &b2); // error
然后,要消除错误,您只需实现该功能即可。不需要 virtual
:
class B {
void foo(B*) {/*do something*/}
};
B b1;
B b2;
b1.foo(&b2); // ok
call_foo(&b1, &b2); // ok
但是,为什么我不能为此使用虚函数?
想象一下以下场景:
struct A {virtual void foo(A*)=0;};
// Imagine if the language allowed this:
struct B {void foo(B*) override{}};
struct C {void foo(C*) override{}};
// (...)
// I create a vector of objects, and insert three of them in this vector.
std::vector<A*> objects;
// Note that foo is well-defined only for the first two.
objects.push_back(new B();)
objects.push_back(new B();)
objects.push_back(new C();)
// Then I shuffle the vector
std::shuffle(objects.begin(), objects.end());
// At least one of these three lines should give a compiler error.
// Which one(s)?
objects[0]->foo(objects[1]);
objects[0]->foo(objects[2]);
objects[1]->foo(objects[2]);
但是我需要函数是虚的,我需要类型安全!
虚函数是一种运行时机制。您必须在运行时检查类型。 dynamic_cast
转换成你想要的类型,如果转换成returnsnullptr
,你的类型就错了。然后您可以抛出异常或打印一些诊断消息。
我了解到您的问题更多是关于语法的。你有什么是对的,只要传递一个B类型的对象。定义仍然会说A但是它会很乐意接受派生class。您不需要对此进行任何特殊定义。
class A {
public:
virtual void foo(A* a) = 0;
};
class B : public A {
public:
void foo(A* a)
{
if (dynamic_cast<B*> (a) == NULL)
std::cout << "wrong type, expecting type B\r\n";
}
};
class C : public A {
public:
void foo(A* a)
{
if (dynamic_cast<C*> (a) == NULL)
std::cout << "wrong type, expecting type C\r\n";
}
};
int main()
{
B * b1 = new B;
B * b2 = new B;
C * c1 = new C;
C * c2 = new C;
b2->foo(c1); // bad
c1->foo(b1); // bad
b2->foo(b1); // good
delete b1;
delete b2;
delete c1;
delete c2;
}
另见 dynamic_cast。