20

我试图弄清楚当派生类将虚函数声明为私有时会发生什么。以下是我写的程序

#include <iostream>
using namespace std;
class A
{
    public:
        virtual void func() {
        cout<<"A::func called"<<endl;
    }
    private:
};
class B:public A
{
    public:
    B()
    {
        cout<<"B constructor called"<<endl;
    }
    private:
    void func() {
        cout<<"B::func called"<<endl;
    }
};
int main()
{
    A *a = new B();
    a->func();
    return 0;
}

令人惊讶的是(对我来说)输出是:

B constructor called
B::func called

这是否违反了该功能的私有访问设置。这是预期的行为吗?这是标准的解决方法还是漏洞?通过 VTABLE 解析函数调用时是否绕过访问级别?

对此行为的任何见解都会非常有帮助。

此外,还提到私有覆盖虚拟成员将阻止其他类继承它。即使这样也有问题。修改上述程序以包括:

class C: public B
{
    public:
    void func() {
        cout<<"C::func called"<<endl;
    }
};

和主要测试程序:

int main()
{
    A *a = new C();
    a->func();
    return 0;
}

输出是:

C::func called
4

3 回答 3

13

这是定义明确的行为。如果a是 aB*这将无法编译。原因是成员访问由编译器静态解析,而不是在运行时动态解析。许多 C++ 书籍建议您避免这样的编码,因为它会使经验不足的编码人员感到困惑。

于 2009-06-30T04:31:35.053 回答
11

行为是正确的。每当您将函数声明为“虚拟”时,都会指示编译器生成虚拟调用,而不是直接调用此函数。每当您在后代类中重写虚函数时,您都指定了该函数的行为(您不会更改那些依赖于“父”接口的客户端的访问模式)。

更改后代类中虚函数的访问模式意味着您希望对那些直接使用后代类的客户端(依赖“子”接口)隐藏它。

考虑这个例子:

void process(const A* object) {
   object->func();
}

“进程”函数依赖于父接口。它应该适用于任何从 A 公开派生的类。您不能从 A 公开派生 B(说“每个 B 都是 A”),而是隐藏其接口的一部分。那些期望“A”的人必须得到一个功能齐全的“A”。

于 2009-06-30T08:16:12.713 回答
3

好吧,您正在调用A::func()whichpublic虽然在B对象中它被B::func(). 这是一种常见的模式,具有以下含义:

  • func不打算在派生B对象上调用

  • func不能在派生自的类中被覆盖B

于 2009-06-30T04:36:22.043 回答