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 的对象(b1b2,或 c1c2,而不是 b1c2).我也想尽可能以一般方式访问此距离函数。

--编辑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