gpt4 book ai didi

c++ - 覆盖虚拟函数和继承

转载 作者:搜寻专家 更新时间:2023-10-31 01:27:48 25 4
gpt4 key购买 nike

我无法完全理解C++中的重写虚拟函数以及调用此类函数时的确切情况。我正在阅读Bjarne Stroustrup使用C++编写的PPP,他提供了以下示例来展示重写和虚拟函数:

struct B {
virtual void f() const { cout<<"B::f; }
void g() const { cout << "B::g"; } //not virtual
};

struct D:B {
void f() const { cout<<"D::f"; } //overrides B::f
void g() { cout<<"D::g"; }
};

struct DD:D {
void f() { cout<<"DD::f"; }
void g() const { cout<<"DD::g";}
};

void call(const B& b) {
//a D is kind of B, so call() can accept D
//a DD is kind of D and a D is a kind of B, so call() can accept a DD
b.f();
b.g();
}

int main() {
B b;
D d;
DD dd;

call(b);
call(d);
call(dd);

b.f();
b.g();
d.f();
d.g();
dd.f();
dd.g();
}

输出: B::f B::g D::f B::g D::f B::g B::f B::g D::f D::g DD::f DD::g
我了解call(b)如何输出简单的 B::f B::g

现在为 call(d)。我不太清楚为什么,但是 call()似乎可以将 B的派生类作为参数。好。因此,在 call()中,由于 b.f()覆盖了 d.f(),因此 D::f变为 B::f。实际上输出显示 D::f。但是 D::g不会覆盖 B::g,并且由于我无法理解的原因,在我看来执行 D::gcall(d)没有任何作用-在这种情况下输出 B::g

接下来,我们执行 call(dd),输出 D::f B::g。应用与上述相同的逻辑(?),很明显 DD::f不会覆盖 D::f-不能是const-,而且 DD::g不会覆盖 D::gB::g,因为 virtual都不是。

接下来发生的事情使我感到困惑。每次单独调用 b.f()b.g()d.f()d.g()dd.f()dd.g()输出都会好像根本没有覆盖!
例如,几秒钟前在call()中的 d.g()输出 D::g时, d.g()如何输出 B::g
另一个,当在 dd.f()中的 DD::f中输出时, dd.f()如何输出 call()输出 D::f

可以肯定地说,我在这里缺少重要的东西,为此我需要帮助。

最佳答案

请耐心阅读,以获取问题的答案。

继承是一个类的对象继承另一个类的对象的属性和行为的概念。

基本介绍

  • 父类也称为基类或父类(super class)。
  • 子类也称为派生类或子类。
  • 可以通过基类引用派生类的对象。

  • 例如
    #include <iostream>
    using namespace std;
    class Base {};
    class Derived : public Base {};
    int main() {
    Base *b = new Derived(); // This is completely valid
    return 0;
    }

    C++中的方法重写

    让我们举一个基本的例子
    #include <iostream>
    using namespace std;
    class Base {
    public:
    void display() { cout << "Base display called\n"; }
    };
    class Derived : public Base {
    public:
    void display() { cout << "Derived display called\n"; }
    };
    int main() {
    Base b;
    b.display();
    Derived d;
    d.display();
    Base *bptr = &d;
    bptr->display();
    return 0;
    }

    输出:
    Base display called
    Derived display called
    Base display called

    现在,从上面的示例中,您可能已经猜到派生类将覆盖基类,但事实并非如此。输出(第三行)显示调用了基类函数,因为该函数不是虚拟的。

    C++中的虚函数

    您可以通过在函数的开头添加“virtual”关键字来使该类的任何函数虚拟化。

    让我们考虑一下虚函数示例
    #include <iostream>
    using namespace std;
    class Base {
    public:
    virtual void display() { cout << "Base display called\n"; }
    };
    class Derived : public Base {
    public:
    void display() { cout << "Derived display called\n"; }
    };
    int main() {
    Base b;
    b.display();
    Derived d;
    d.display();
    Base *bptr = &d;
    bptr->display();
    return 0;
    }

    输出:
    Base display called
    Derived display called
    Derived display called

    通过上面的示例(输出的第3行),很明显,您可以使用C++中的虚函数机制来实现方法覆盖。

    使功能虚拟化有什么作用?

    正常函数和虚拟函数之间的区别在于,正常函数在编译时解析,也称为静态绑定(bind),而虚拟函数在运行时解析,也称为动态绑定(bind)或后期绑定(bind)。由于基类显示功能是虚拟的,因此在运行时可以解析调用哪种方法(基类显示或派生类显示方法)。通过阅读有关v-table的信息,您可以深入了解虚拟功能机制。

    问题答案
  • call (d)。我不太清楚为什么,但似乎call()可以将B的派生类作为参数。
  • 基类
  • 可以引用派生类对象
  • 但是D::g不会覆盖B::g,由于我无法理解的原因。
  • 因为g()在基类中不是虚拟的。只能覆盖虚拟功能。 v-table的条目仅包含虚拟功能,因此可以在运行时覆盖它们。
  • 电话(dd)
  • 由于DD中的f()是非常数函数,因此f()(DD中的非常数f())不是父类D的重写方法。并且由于基类B引用了它,调用bf()会调用被D覆盖的const f()。这样就打印了D::f。
  • 如果f()是DD中的const方法,则将发生以下情况:调用f()时,首先在基类B中搜索该函数,
    由于f()是虚拟的,因此使用v表指针,可解决派生类D中重写的f()函数。但是由于f()在类D中不是虚拟的,因此无法解析DD中重写的f()。因此D::f被打印。
    但是对于g(),基类本身没有虚拟g(),因此无法解析其派生类中的重写函数。因此,B::g被打印出来。
  • 发生上述多态性是因为派生类被其基类(父类)引用,但是在上一次调用中没有这样的事情。所有对象均由其各自的类引用,因此将调用其适当的方法。

  • 考虑这一点的一种基本逻辑是,首先在引用类中查找该函数,如果它是虚拟的,则将在派生类中搜索该函数(如果要引用的对象是子类)。如果派生类被覆盖,则将调用派生方法,否则将调用基本方法。您可以进一步将应用于概念的概念扩展到基类,派生类,以及检查该函数是否为虚函数,然后检查该函数是否为虚函数并且派生对象(派生的 child ,基的孙子) ) 等等。

    希望这可以澄清。

    关于c++ - 覆盖虚拟函数和继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52768702/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com