不同场景下的虚拟关键字

时间:2011-01-07 05:45:45

标签: c++ virtual

我找出了使用virtual的两种不同情况。

  • 如果baseClass的函数定义为virtual,则derivedClass将覆盖该函数。

  • baseClass::~baseClass()应该定义virtual,如果有任何类派生的话。这意味着,派生类破坏首先发生,然后是基类破坏。

是否还有其他使用virtual的情况?

7 个答案:

答案 0 :(得分:1)

virtual在应用于成员函数时始终具有相同的含义。当成员函数为virtual时,意味着将动态调度对该成员函数的调用。

即,将根据动态类型(实际类型)选择要调用的函数,而不是静态类型。根据对象的实际类型,将调用虚函数的最终覆盖。

析构函数“不同”的唯一原因是派生类析构函数的名称与基类析构函数不同。派生类析构函数的行为不受它被声明为virtual的影响,但是:派生类析构函数在运行后总是调用基类析构函数。


作为virtual函数的行为示例:

struct B {
    void f() { }
    virtual void g() { }
};

struct D : B {
    void f() { }
    virtual void g() { }
};

int main() {
    B* p = new D();
}

表达式*p的动态类型为D,因为p指向的对象的实际类型为D。您使用new D()创建了它,因此它的动态类型为D

表达式*p的静态类型是B,因为这是代码中指定的类型。如果不运行程序或评估分配给p的内容,编译器就不知道*p给出的对象的派生类型最多;它只知道无论它是什么,都是B或从B派生的东西。

如果您致电p->f(),则会静态调度呼叫,因为B::f不是virtual。编译器查看*p的静态类型,并根据(B::f)选择要调用的函数。

如果您致电p->g(),则会动态调度该呼叫,因为B::gvirtual。在运行时,检查对象的动态类型(在许多常见实现中使用vtable),并调用最终的覆盖。在这种情况下,最终的覆盖是D::g,因为D是覆盖B::g的派生程度最高的类(如果有另一个派生自D的类,它可以选择覆盖B::g也是如此。

答案 1 :(得分:1)

还有虚拟继承,其中基类由间接引用。

In C++, what is a virtual base class?

答案 2 :(得分:0)

答案 3 :(得分:0)

虚拟继承是一个很大的问题。除其他外,它解决了多重继承引入的重复成员的问题。谷歌“可怕的钻石图案”的详细信息。

答案 4 :(得分:0)

虚拟继承的示例,

class UltimateBase{};
class Base1 : public virtual UltimateBase{};
class Base2 : public virtual UltimateBase {};

class Derived : public Base1, public Base2{};

因此,Derived类的实例可能只有UltimateBase个类的子对象

答案 5 :(得分:0)

实际上还有一个虚拟的小怪癖。假设你有一个类,你想成为例如java,c#,php和actionscript会调用abstract,但是这个类没有纯虚方法。那么你可以在类声明中将析构函数声明为纯虚方法,但仍然可以实现它。这是一个例子:

#include <iostream>

class MyAbstractClass
{
    public:
        MyAbstractClass(int i);
        virtual ~MyAbstractClass() = 0;
        int getI() const;
    private:
        int m_i;
};

class MyConcreteClass : public MyAbstractClass
{
    public:
        MyConcreteClass(int i);
        ~MyConcreteClass();
};

MyAbstractClass::MyAbstractClass(int i) :
    m_i(i)
{
    std::cout << "constructor of MyAbstractClass\n";
}

MyAbstractClass::~MyAbstractClass()
{
    std::cout << "destructor of MyAbstractClass\n";
}

int MyAbstractClass::getI() const
{
    return m_i;
}

MyConcreteClass::MyConcreteClass(int i) :
    MyAbstractClass(i)
{
    std::cout << "constructor of MyConcreteClass\n";
}

MyConcreteClass::~MyConcreteClass()
{
    std::cout << "destructor of MyConcreteClass\n";
}

int main(int argc, char* argv[])
{
    MyConcreteClass a(5);
    std::cout << a.getI();
    std::cout << std::endl;
    MyAbstractClass b(5); //compile error, can't instantiate a class with abstract functions

    return 0;
}

答案 6 :(得分:0)

virtual关键字为函数提供后期绑定。在后期绑定中,函数调用直到运行时才绑定到任何东西。在多态和运行时,类查找其v表以确定要调用的重载函数。当它的类是多态的时,析构函数通常是virtualvirtual可能与inline最相反,与extern非常相似(至少在我看来)。