一旦函数名称被声明为“虚拟”,它是否可以再次变为非虚拟?

发布于 2024-11-26 21:31:04 字数 1260 浏览 0 评论 0原文

假设我有一些类链,其中每个类都是从它之前的类派生的。无论出于何种原因,他们都喜欢对某些成员函数使用相同的名称。有点像这样:

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

所有这些都是 super。我要么调用与对象类型关联的函数,要么调用与指针类型关联的函数......

或者当然我可以使该函数成为“虚拟”函数。然后,如果我从某个对象调用该函数,我的行为将不会发生变化;但是,如果我从某个指针调用该函数,那么我不仅会调用该指针类型的函数,而且实际上会调用该指针所指向的对象类型的函数。到目前为止,一切都很好。

我什至可以在继承链的中途更改为虚拟。假设我在类 C2 内的函数 f 之前放置了一个虚函数。现在该函数已成为虚拟的(即,当从指针调用时,它使用指向对象的类型而不是指针类型来解析函数调用),不仅对于它自己的类,而且对于所有将来的类都是从中衍生出来的。

我的问题是:一旦一个函数被声明为虚拟函数(在继承链中的某个点),它是否可以恢复为非虚拟函数(在继承链的更下游)?

澄清一下:当我说恢复到非虚拟行为时,我的意思是当我从指针调用函数时,它将使用指针的类型来解析函数调用(而不是指针指向的对象类型) )。

So let us say I have some chain of classes where each class is derived from the class before it. For whatever reason, they all like to use the same name for some member function. Kinda like this:

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"; }; };

Obviously, if I just declare some objects, then call the function f from them, all will call the function associated with their respective object-type:

C1 c1; c1.f(); // prints C1
C2 c2; c2.f(); // prints C2
C3 c3; c3.f(); // prints C3

Now, if I declare some pointers-to-objects, then call the function f from them, all will call the function associated with their respective pointer-type:

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

All of this is super. I either call the function associated with the object's type or I call the function associated with the pointer's type...

Or of course I could make the function 'virtual'. Then if I call the function from some object, I will get no change in behavior; however, if I call the function from some pointer, then I won't just call the function for the pointer's type, I will actually call the function for the object-type that the pointer is pointing to. So far so good.

I can even make the change to virtual mid-way through the chain of inheritance. Let's say I put a virtual before the function f inside of class C2. Now the function has been made virtual (i.e. when called from pointers, it uses the object-pointed-to-type instead of the pointer-type to resolve the function call), not only for its own class, but for all future classes that are derived from it.

My question is this: Once a function has been declared virtual (at some point in the chain of inheritance) can it ever be reverted back to being non-virtual (further down the chain of inheritance)?

For clarification: When I say revert back to non-virtual behavior, I mean that when I call the function from a pointer, it will use the pointer's type to resolve the function call (and not the object-type that the pointer is pointing to).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

怂人 2024-12-03 21:31:04

一旦函数被声明为虚拟函数(在继承链中的某个点),它是否可以恢复为非虚拟函数(在继承链的更下游)?

不会。一旦函数签名在继承过程中的某个位置被设为虚拟,它将保持这种状态:派生类中具有相同签名的每个函数都将是虚拟的以及。

解决这个问题的一种方法是使用(滥用?)模板方法模式

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

Once a function has been declared virtual (at some point in the chain of inheritance) can it ever be reverted back to being non-virtual (further down the chain of inheritance)?

No. Once a function signature has been made virtual somewhere along the line of inheritance, it will stay that way: every function with the same signature in a derived class will be virtual as well.

One way to get around this is to use (abuse?) the 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
};

In this inheritance tree, the pointer type will determine which foo is called; if it's the one from Base, the pointed-to object's type will determine which doFoo is called.

江南月 2024-12-03 21:31:04

简单的答案:不。

如果您知道虚拟函数在 C++ 中如何工作,您就会知道这是不可能的。简单地说,每个类都有一个表,其中包含虚函数列表和重写函数的地址。当您使用 virtual 覆盖函数时,该函数将列在声明它的类的表中的列表中,因此它将出现在继承链的其余部分中。

Simple answer: No.

If you know how the virtual functions work at C++, you would had know that it is not possible. Putting it simple, for each class there is a table containing a list of virtual functions and the addresses of the overridden functions. When you override a function with virtual, that function will get listed at that list in the table of the class it was declared at, so it will be there for the rest of the inheritance chain.

手心的海 2024-12-03 21:31:04

简单答案:不。

一旦将其设为虚拟,它在所有派生类中都将是虚拟的(即使以后不再使用 virtual 关键字)。

因此,一些较新的语言强制您使用另一个关键字,以便您知道您正在覆盖虚拟方法(因为它可能不明显),但没有一种语言允许您取消虚拟方法。

Simple Answer: No.

Once it has been made virtual it will be virtual in all derived classes (even if the virtual keyword is not used later).

For this reason several newer languages force you to use another keyword so that you know that you are overiding a virtual method (as it may not be obvious), but none allow you to un-virtual a method.

梦在深巷 2024-12-03 21:31:04

正如其他人已经说过的,您无法对函数进行去虚拟化,但是绝对没有什么可以阻止您调用所需函数的特定版本,只要它对于底层类是合法的:

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

As everyone else has already stated, you can't de-virtualise a function, but there's absolutely nothing to stop you from calling the specific version of the function you require, as long as it's legal for the underlying class:

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
花海 2024-12-03 21:31:04

不。

用模板方法解决这个问题:

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 的行为。

No.

Solving this with template method:

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(); }};

now C3 and it's descendants have C1's behaviour.

横笛休吹塞上声 2024-12-03 21:31:04

不确定,它会回答你 - 你可以使用 VC++ 中的 novtable 属性。

Not sure, it it answers you - you may use novtable attribute in VC++.

忆梦 2024-12-03 21:31:04

不,它无法恢复。我从来不需要这个,也无法想象现实生活中需要这个的类层次结构的例子。

此外,建议避免深层层次结构,例如 B.Straustrup。

no, it cannot be reverted. I never needed this and cannot imagine real-life example of class hierarchy that needs this.

also, it's recommended to avoid deep hierarchy, e.g. by B.Straustrup.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文