【问题标题】:Overriding private functions覆盖私有函数
【发布时间】:2018-07-24 06:08:32
【问题描述】:

我正在研究一个从类(称为 B 类)派生的类(称为 D 类),并将被另一个类(称为 E 类)继承。我希望 E 类的实例能够访问其父类的所有公共函数,但我特别想防止 E 类覆盖父类中的任何虚函数。

我认为通过覆盖 D 类的私有部分中的函数可以防止派生类被覆盖,但情况似乎并非如此。这将编译并运行 E 类中的函数:

#include <iostream>

//an abstract base class
class b
{
public:
    virtual ~b() = default;
protected:
    virtual void print() = 0;
    virtual void print_two()
    {
        std::cout << "base class" << std::endl;
    }
};

class d: public b
{
public:
    virtual ~d() = default;
private:
    void print() override // note this is not virtual -adding final here does the trick
    {
        std::cout << "class d" << std::endl;
    }
    using b::print_two; //this doesn't work either
};

class e : public d
{
public:
    void print() override //why is this possible given the function it's overriding is private and non-virtual?
    {
        std::cout << "class e" << std::endl;
    }

    void print_two() override //why is this possible given the function it's overriding is private and non-virtual?
    {
        std::cout << "print two class e" << std::endl;
    }
};


int main()
{
    e foo;
    foo.print();
    foo.print_two();

}

所以有两个问题: 为什么我可以覆盖在祖父母中是虚拟但在父母中不是虚拟的函数? 为什么我可以覆盖在祖父母中受保护但在父母中为私有的函数?

我在 g++ 和 clang 中尝试过,它编译时没有错误或警告 (-Wall -Wextra -Wpedantic)

【问题讨论】:

  • d,试试这个:virtual void print() override final { ... }
  • "注意这不是虚拟的" 它实际上虚拟的,所以你的一个前提是错误的。 (如果它不是虚拟的,您将无法 override 它。)
  • 一旦函数被标记为virtual,它就是sticky。它与所有子类中的函数保持一致。无法取消虚拟化函数。
  • 另外,virtual 与访问级别无关。覆盖私有的东西可能非常有用(请参阅模板方法模式。)

标签: c++ c++11 inheritance


【解决方案1】:

为什么我可以覆盖在祖父母中是虚拟但在父母中不是虚拟的函数?

没有这样的事情。一旦函数被声明为虚拟,它在所有派生类中都是隐式虚拟的。接近“取消虚拟化”成员函数的唯一方法是声明它final。这将无法进一步覆盖它,但是当通过祖先引用/指针调用时,它仍然会受到动态调度的影响。但是,任何通过声明它final 或其后代的子节点的访问都可以静态解决。

为什么我可以覆盖在祖父母中受保护但在父母中为私有的函数?

访问说明符不会影响名称的可见性。它们只影响可以使用该名称的位置。定义相同的函数将覆盖父级的实现并由动态调度调用。同样,因为这不属于“访问”名称的范围。

如果父级的实现 (Parent::foo) 在父级中是私有的,则子级不能引用父级的实现,使用该接口的外部代码也不能引用。它确保仅以受控方式调用该函数。对于测试前后条件非常有用。

【讨论】:

  • 这是绕过可见性检查并调用私有函数的技巧之一吗?
  • @Incomputable - 你是什么意思?
  • 我听说私有函数仍然可以在类之外直接调用。我最初的猜测是 ADL 技巧,但在阅读了这篇文章后,我得到了更强的候选人。可能是我很困惑。
  • @Incomputable - 你没有困惑。派生类可以在自己的实现中放置它想要的任何访问说明符。但是父实现被锁定为私有。所以只绕了半圈。
  • @Incomputable - The standard has an example。也可以在孩子身上公开。我猜您阅读的建议与该案例有关。当孩子认为它定义了一个私有函数时,它只是一种被外部调用的方式。
猜你喜欢
  • 1970-01-01
  • 2013-05-07
  • 2017-06-08
  • 2020-12-17
  • 2010-10-03
  • 2014-05-28
  • 2015-01-25
  • 1970-01-01
相关资源
最近更新 更多