C++虚函数的实现?

11

如果我在C++中有:

class A {
    private: virtual int myfunction(void) {return 1;}
}

class B: public A {
    private: virtual int myfunction(void) {return 2;}
}
如果我从类B中的myfunction定义中移除virtual关键字,那么是否意味着如果我有一个基于类B的类C,则无法覆盖myfunction,因为它会被静态编译?
此外,当在public和private之间切换时会发生什么,让我很困惑。如果我将类B中myfunction的定义更改为public(而在类A中保持为private),这是一种严重的错误吗?我认为虚函数需要保持相同的类型,所以这是不合法的,请告诉我是否正确。
谢谢!

5
派生类的方法不需要显式地写出 "virtual" 关键字。你可以省略它,但是最好还是写上,这是良好的编程风格。 - varnie
4个回答

18

带有“virtual”的第一个定义是最重要的。从基类继承的函数在派生类中变为虚函数,这意味着您不需要在重新实现的虚函数调用中使用“virtual”。如果基类中的函数签名不是虚拟的,但在派生类中是虚拟的,则基类没有多态行为。

class Base
{
    public:
    void func(void){ printf("foo\n"); }
};
class Derived1 : public Base
{
    public:
    virtual void func(){ printf("bar\n"); }
};
class Derived2 : public Derived1
{
    public:
    /*  reimplement func(), no need for 'virtual' keyword
        because Derived1::func is already virtual */
    void func(){ printf("baz\n"); } 
};

int main()
{
    Base* b = new Derived1;
    Derived1* d = new Derived2;

    b->func(); //prints foo - not polymorphic
    d->func(); //prints baz - polymorphic
}

不用谢。我还应该补充一下,“Base* b”实际上是Derived1。在需要时使用dynamic_cast进行安全的向下转换。 - Mads Elvheim
但是作者的示例涵盖了私有成员。 - bua
@bua:与使用模板时的依赖/独立名称查找不同,虚拟调用的名称查找不受访问说明符的影响。 - Mads Elvheim
示例非常好。 - Jongbin Park

7
一旦在基类中将函数设置为虚函数,它就会在每个其他子类中都是虚函数。 public、protected和private不影响函数的虚拟性质。

谢谢!感谢可见性信息。 - atp
通过将虚函数设置为protected或private,您可以向类的用户暗示他们是否需要在子类中调用它们。http://www.gotw.ca/publications/mill18.htm - Chris Bednarski

1
如果您从类B中的myfunction定义中删除virtual,编译器将为您添加它,以填充多态类型的V表。
但是,请注意,这样做会导致您只能访问类A的公共成员(类B:public A)。
定义如下:
class B: private A
{

}

会导致类A的所有成员(即使是公共成员)对于类B来说都变为私有。 简单来说,您将无法访问A的公共成员。

为了解决这个问题,您可以声明一些友元:

class A
{
    private:
        friend class B;
}

更多精彩信息在这里


答案让我有些困惑。从我的理解中,类B不能访问它从类A继承的公共成员,但是可以通过声明B为A的友元来解决这个问题,但我认为这并不正确。B可以访问A的公共和受保护成员,但B的用户无法访问A的公共成员,并且这也不能通过friend声明来解决。我是否误解了什么? - stefaanv
是的,你在我的例子中错过了私有继承。 - bua
1
好的,那我建议您重新阅读一下链接中的信息:类B可以访问其私有继承的类A中的所有公共和受保护成员,无论是否是友元。 - stefaanv
乐观地说,也许他们的意思不是B不能访问从A继承的成员,而是B的外部用户不能通过B的接口访问A的(以前公开的)成员。无论如何,这个答案措辞不当,与问题的相关性值得怀疑。 - underscore_d

0

virtual 的行为是影响当您有一个指向子类型对象的指针时调用哪个方法。例如:

B* obj = new B;
A* base = obj;

当您调用obj->myfunction()时,发生的情况取决于A是否声明myfunction为虚函数。如果它不是虚函数,则推理如下:我们有一个类型为A的指针,因此调用在A中定义的函数,结果为1。然而,如果Amyfunction定义为虚函数,则会在运行时基于实际对象的类型而不是指针类型执行查找;由于对象实际上是B,因此使用在B中定义的实现,结果为2。
更多信息可以在C++ FAQ Lite关于虚函数的部分中找到。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接