C ++使用抽象类作为参数来覆盖虚拟方法

时间:2018-09-19 02:02:40

标签: c++ virtual abstract-methods

我有一个下面的抽象类

class A {
public:
    virtual void foo(A* a) = 0;
}

以及从该类继承的几个类。例如

class B : public A {
public:
    void foo(A* a); // implementation in a separete file
}

但是,我只希望类B接受自己作为foo中的参数

void foo(B* b);

是否可以在C ++中做到这一点? 我考虑过模板,但是语法允许太多的灵活性。可以写class B: public A<B>,但是我想用class B: public A<C>编译器错误。

-编辑-

似乎我对抽象类的使用是不合理的。让我澄清一下我的情况。

我在单独的函数中利用A的多态行为。除此之外,我想定义一个函数,该函数接受相同类型的参数,例如上面的参数。我正在尝试编写一个函数,该函数定义派生类的两个对象之间的距离。距离仅在同一类(b1b2c1c2,而不是b1c2的对象之间定义) 。我也想以一种通用的方式来访问这个距离函数。

-编辑2-

Cássio显示了为什么无法执行基于编译器的检查。 zar的解决方案通过运行时错误检查为代码增加了一些结构。

2 个答案:

答案 0 :(得分:1)

那不是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"

您尝试使用它的方式,失去了这一好处,而您却一无所获地获得了虚拟函数的性能优势。实例化一个没有实现某些纯虚函数的类是一个错误,实际上只是为了防止格式错误的程序。

如果要确保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]);


但是我需要功能是虚的,并且我需要类型安全!

虚拟函数是一种运行时机制。您将必须在运行时检查类型。 zar's answer已经很好地涵盖了这一点,因此我将不赘述。总结一下:只需将dynamic_cast插入所需的类型,如果强制转换返回nullptr,则您输入的类型错误。然后,您可以引发异常或打印一些诊断消息。

答案 1 :(得分:1)

我了解您的问题更多是关于语法的。您所拥有的是正确的,只需传递一个B类型的对象即可。该定义仍然会说A,但是很高兴采用派生类。为此,您不需要任何特殊的定义。

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