我找出了使用virtual
的两种不同情况。
如果baseClass
的函数定义为virtual
,则derivedClass
将覆盖该函数。
baseClass::~baseClass()
应该定义virtual
,如果有任何类派生的话。这意味着,派生类破坏首先发生,然后是基类破坏。
是否还有其他使用virtual
的情况?
答案 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::g
为virtual
。在运行时,检查对象的动态类型(在许多常见实现中使用vtable),并调用最终的覆盖。在这种情况下,最终的覆盖是D::g
,因为D
是覆盖B::g
的派生程度最高的类(如果有另一个派生自D
的类,它可以选择覆盖B::g
也是如此。
答案 1 :(得分:1)
还有虚拟继承,其中基类由间接引用。
答案 2 :(得分:0)
另请阅读C ++ - FAQ:What is the "dreaded diamond"?
答案 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表以确定要调用的重载函数。当它的类是多态的时,析构函数通常是virtual
。 virtual
可能与inline
最相反,与extern
非常相似(至少在我看来)。