Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 函数名声明为';虚拟';,它还能再变成非虚拟的吗?_C++_Virtual - Fatal编程技术网

C++ 函数名声明为';虚拟';,它还能再变成非虚拟的吗?

C++ 函数名声明为';虚拟';,它还能再变成非虚拟的吗?,c++,virtual,C++,Virtual,让我们假设我有一些类链,其中每个类都是从它前面的类派生的。无论出于何种原因,它们都喜欢对某些成员函数使用相同的名称。有点像这样: class C1 { public: void f() { cout<<"C1"; }; }; class C2 : public C1 { public: void f() { cout<<"C2"; }; }; class C3 : public C2 { public: void f() { cout<<

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

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* 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前面放了一个虚函数。现在,函数已被虚拟化(即,当从指针调用时,它使用指向类型的对象而不是指针类型来解析函数调用),不仅针对它自己的类,而且针对从它派生的所有未来类

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

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

简单回答:否

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

由于这个原因,一些较新的语言强制您使用另一个关键字,以便您知道您正在过度隐藏虚拟方法(因为这可能并不明显),但没有一种语言允许您取消虚拟方法。

简单回答:否

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

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

否。一旦函数签名在继承过程中的某个地方被创建为虚拟的,它将保持这种状态:派生类中具有相同签名的每个函数也将是虚拟的

解决这一问题的一种方法是使用(滥用?)以下工具:


在这个继承树中,指针类型将决定调用哪个
foo
;如果是来自
Base
的对象,则指向对象的类型将确定调用哪个
doFoo

否,无法还原。我从不需要这个,也无法想象现实生活中的类层次结构示例需要这个

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

使用模板方法解决此问题:

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

类C1{public:virtualvoid f(){g();};void g(){std::cout不确定,它会回答您-您可以在VC++中使用attribute。

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

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

通过
C1*p1=&C1;
等,您的意思必须是
C1*p1=&C1;
(即,您不尝试获取类的地址)。注意,你不能让函数在链的中间变为虚拟函数。你要隐藏原来的函数并引入一个新的函数。你只能在该点以下得到多态行为;在该点以上得到静态调度。@Jimmy:当你说“还原回”时,你期望得到什么行为?@Dennis:这就是我说的意思“在供应链的中途更改为虚拟"--在引入关键字virtual之前,函数将对所有类静态运行,然后对具有virtual关键字的类以及从该类派生的所有类执行多态操作。这是可能的,是吗?@larsmans:很好的调用,我对剪切和粘贴有点疯狂。修复了。C++0x是否添加了
final
keyword是否取消函数的虚拟化?@Kerrek SB:IIUC,
final
完全禁止在基类中使用签名。呃……这意味着什么?:-)对foo()不起作用如果你有一个指向Derived2的Base*指针。@yi_H:它的工作原理是,它完全符合我对非
虚拟
成员函数的期望:call
Base::Foo
。只是探索语言的局限性,我相信比亚恩会同意。我真的怀疑。你在实际项目中不需要这个,对吗?我也是。顺便说一句,这是一个当我不喜欢不加评论的下注时,我最不知道原因是什么:当这个特性有用时,下垂者经常遇到例子,或者只是我的风格。讨论VSt表是不相关的实现细节。C++可能有一个关键字,比如“代码>实际< /C>”,重写“代码>虚拟,编译器将有足够的信息来决定每个成员函数声明是否应该是
虚拟的
。回答不好:使用实现细节作为某项工作的参数并不是一个真正有效的原因。在实现上会发生什么
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(); }};
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