虚函数和多态

rit*_*mbo 1 c++ polymorphism virtual

假设我有这个:

class A
{
    public:
    virtual int hello(A a);
};

class B : public A
{
   public:
   int hello(B b){ bla bla };
};
Run Code Online (Sandbox Code Playgroud)

所以,它是一个抽象类.

1)在B类中,我正在定义一个方法,它假设覆盖了A类.但它的参数略有不同.我不确定这个,这是对的吗?也许是因为多态性,这是可以的,但它相当令人困惑.2)如果我这样做:A a =新B;,然后是a.hello(lol); 如果"lol"它不是B类,那么它会给出编译错误?如果它是来自另一个C类的C类(C类:公共A),会发生什么?

我对覆盖和虚拟的东西感到困惑..我发现的所有例子都使用没有参数的方法.

任何答案,链接,或任何它的赞赏.

谢谢

pd:对不起我的英语

Ste*_*sop 7

你的B类没有覆盖 A中的成员函数,它会重载它.或者尝试无论如何,看看稍后隐藏的一点.

覆盖是指派生类从基类定义其自己的虚拟成员函数版本.重载是指定义具有相同名称的不同函数.

当对具有基类类型的指针或引用进行虚拟调用时,它只会"考虑"派生类中的覆盖,而不是重载.这是必不可少的 - 对于调用者处理B的实例,好像它完成了A可以做的所有事情(这是动态多态和虚函数的要点),它的hello函数需要能够采用任何类型的对象.阿hello函数只需要B型的对象,而不是任何A,是更限制性的.它不能扮演A的hello功能,所以它不是一个覆盖.

如果您通过调用helloA和B进行实验,传递A或B类型的对象,您应该能够看到差异.A有一个功能A(你没有定义,所以如果你调用它,那么你的程序将无法链接,但你可以修复它).B有一个带B的函数.它们碰巧有相同的名字,当然因为B来自A,你可以将B传递给带有A的函数.但是B的函数不能作为虚拟调用中的覆盖.

可以在B对象上调用A的函数,但只能通过引用或指向A的指针.C++的一个特性是helloB中的定义将定义隐藏在A中.如果重载是您想要的,则可以取消- 通过添加using A::hello;到类B来隐藏基类函数.如果覆盖是你想要的,你必须定义一个采用相同参数的函数.例如:

#include <iostream>

class A
{
    public:
    virtual int hello(A a) {std::cout << "A\n"; }
    virtual int foo(int i) { std::cout << "A::Foo " << i << "\n"; }
};

class B : public A
{
   public:
   using A::hello;
   // here's an overload
   int hello(B b){ std::cout << "B\n"; };
   // here's an override:
   virtual int foo(int i) { std::cout << "B::Foo " << i << "\n"; }
};

int main() {
    A a;
    B b;
    a.hello(a);  // calls the function exactly as defined in A
    a.hello(b);  // B "is an" A, so this is allowed and slices the parameter
    b.hello(a);  // OK, but only because of `using`
    b.hello(b);  // calls the function exactly as defined in B
    A &ab = b;   // a reference to a B object, but as an A
    ab.hello(a); // calls the function in A
    ab.hello(b); // *also* calls the function in A, proving B has not overridden it
    a.foo(1);    // calls the function in A
    b.foo(2);    // calls the function in B
    ab.foo(3);   // calls the function in B, because it is overridden
}
Run Code Online (Sandbox Code Playgroud)

输出:

A
A
A
B
A
A
A::Foo 1
B::Foo 2
B::Foo 3
Run Code Online (Sandbox Code Playgroud)

如果using A::hello;从B中删除该行,则调用b.hello(a);无法编译:

error: no matching function for call to `B::hello(A&)'
note: candidates are: int B::hello(B)
Run Code Online (Sandbox Code Playgroud)


Ben*_*igt 5

一堆很好的答案告诉你发生了什么,我想我应该跳出为什么。

有一个叫做里氏替换原则的东西,它说子类中的函数必须在与基类相同的前提条件后置条件下工作。在这种情况下,该函数必须能够对 A 类型的任何对象进行操作。请注意,由于继承关系,每个 B 都是 A,但并非每个 A 都是 B。因此要替换基本方法,派生类中的新函数可以削弱前置条件或增强后置条件,但不能增强前置条件或削弱后置条件。

您尝试覆盖加强了前提条件,它接受 B,而不是所有 As。

请注意,返回类型允许协方差。如果您的基类返回 A,那么它保证返回值是 A。然后基类可以返回 B,因为每个 B 都是 A。

但对于输入参数,只有逆变满足LSP的理论要求,输入/输出参数是不变的。特别是在 C++ 中,为了重载的目的,所有参数类型都是不变的。