所以,让我们说我有一些类链,其中每个类都是从它之前的类派生出来的。无论出于何种原因,他们都喜欢为某些成员函数使用相同的名称。有点像这样:
class C1 { public: void f() { cout<<"C1"; }; };
class C2 : public C1 { public: void f() { cout<<"C2"; }; };
class C3 : public C2 { public: void f() { cout<<"C3"; }; };
显然,如果我只声明一些对象,然后从它们调用函数f,所有将调用与它们各自的对象类型相关联的函数:
C1 c1; c1.f(); // prints C1
C2 c2; c2.f(); // prints C2
C3 c3; c3.f(); // prints C3
现在,如果我声明一些指向对象的指针,然后从它们调用函数f,所有都将调用与它们各自的指针类型相关联的函数:
C1* p1 = &c1; p1->f(); // prints C1
C1* p2 = &c2; p2->f(); // prints C1
C1* p3 = &c3; p3->f(); // prints C1
C2* p4 = &c2; p4->f(); // prints C2
C2* p5 = &c3; p5->f(); // prints C2
C3* p6 = &c3; p6->f(); // prints C3
所有这些都是超级的。我要么调用与对象类型相关联的函数,要么调用与指针类型相关联的函数...
当然我可以将功能设为'虚拟'。然后,如果我从某个对象调用该函数,我将不会改变行为;但是,如果我从某个指针调用该函数,那么我不会只调用指针类型的函数,我实际上会调用指针所指向的对象类型的函数。到目前为止一切都很好。
我甚至可以通过继承链中途进行虚拟更改。假设我在类C2中的函数f之前放置了一个虚拟。现在该函数已经变为虚拟(即,当从指针调用时,它使用对象指向的类型而不是指针类型来解析函数调用),不仅对于它自己的类,而且对于所有将来的类,来自它。
我的问题是:一旦一个函数被声明为虚拟(在继承链中的某个点),它是否可以恢复为非虚拟(继承链的下方)?
为了澄清:当我说恢复到非虚拟行为时,我的意思是当我从指针调用函数时,它将使用指针的类型来解析函数调用(而不是指针的对象类型)指着)。
答案 0 :(得分:2)
简单回答:不。
如果您知道虚拟函数在C ++中的工作方式,您就会知道它是不可能的。简单来说,每个类都有一个表,其中包含虚函数列表和被覆盖函数的地址。当你用virtual覆盖一个函数时,该函数将被列在它所声明的类的表中的那个列表中,所以它将存在于继承链的其余部分。
答案 1 :(得分:2)
一旦一个函数被声明为虚拟(在继承链中的某个点),它是否可以恢复为非虚拟(继承链的下游)?
没有。一旦在继承行的某个地方创建了一个函数签名virtual
,它就会保持这种状态:派生类中具有相同签名的每个函数也都是virtual
。
解决此问题的一种方法是使用(滥用?)template method pattern:
struct Base {
virtual void doFoo() { bar(); }
void foo() { doFoo(); }
};
struct Derived1 : public Base {
virtual void doFoo() { baz(); } // "overrides" foo via doFoo
};
struct Derived2 : public Base {
void foo() { quux(); } // "un-virtualize" foo by decoupling it from doFoo
};
在这个继承树中,指针类型将决定调用哪个foo
;如果它是来自Base
的那个,则指向对象的类型将决定调用哪个doFoo
。
答案 2 :(得分:1)
简单回答:不。
一旦虚拟化,它将在所有派生类中都是虚拟的(即使以后不使用虚拟关键字)。
由于这个原因,一些较新的语言强迫您使用另一个关键字,以便您知道您正在覆盖虚拟方法(因为它可能不是很明显),但是没有一个允许您取消虚拟方法。
答案 3 :(得分:1)
正如其他人已经说过的那样,你不能对一个函数进行去虚拟化,但是没有什么可以阻止你调用你需要的函数的特定版本,只要它对于底层类是合法的:
C1* p1 = &c1; p1->C1::f(); // prints C1
C1* p2 = &c2; p2->C1::f(); // prints C1, not C2
C1* p3 = &c3; p3->C1::f(); // prints C1, not C3
C2* p4 = &c2; p4->C2::f(); // prints C2
C2* p5 = &c3; p5->C2::f(); // prints C2, not C3
C3* p6 = &c3; p6->C3::f(); // prints C3
答案 4 :(得分:0)
没有
使用模板方法解决此问题:
class C1 { public: virtual void f() { g(); }; void g() { std::cout<<"C1"; }; };
class C2 : public C1 { public: virtual void f() { std::cout<<"C2"; }; };
class C3 : public C2 { public: virtual void f() { g(); }};
现在C3和它的后代有C1的行为。
答案 5 :(得分:0)
不确定,它会回答你 - 你可以在VC ++中使用novtable属性。
答案 6 :(得分:-2)
另外,建议避免深层次,例如作者:B.Straustrup。