【问题标题】:Dynamic binding of private methods: Java vs. C++私有方法的动态绑定:Java vs. C++
【发布时间】:2013-11-24 22:27:15
【问题描述】:

这在 Java 中是不允许的:

class A { 
    public void method() {}
}

class B extends A { 
    private void method() {}
}

它会产生一个编译错误:

error: method() in B cannot override method() in A 
attempting to assign weaker access privileges; was public

但是,这在 C++ 中是允许的:

class A { 
    public:
        virtual void method() {}
};

class B : public A { 
    private:
        void method() {}
};

int main(void) {
    A* obj = new B();
    obj->method(); // B::method is invoked, despite it being private
}

C++ 中这种行为背后的逻辑是什么?

【问题讨论】:

  • Java 针对 90% 的用例进行了优化。 C++ 优化了 10%。
  • Java 更容易掌握。它比 C++ 对你的约束更多,希望大多数“理智”的程序都能适应这些约束,从而减少你犯错的空间。
  • С++ 和 Java 是用于不同目的的不同语言。
  • 实际上,A::method() 正在被调用。作为虚拟,它会按照设计获得多态行为。
  • @sehe 我明白你的意思,但是当你在A::methodB::method 中分别打印时,很难说A::method 正在被调用,只有后一个打印被执行。

标签: java c++ private dynamic-binding


【解决方案1】:

请记住,method 的可见性纯粹在编译时解决,C++ 没有运行时验证器的概念。编译器看到的是一个虚拟的A::method,它不是私有的。具体实现声明为私有的,但这仅在以编译器可见的方式直接调用此实现时才相关,即如果您尝试通过B直接调用它来访问它。

它的逻辑可以用下面的例子来说明:想象一下,如果B不是公开地继承自A,而是私有地——这在C++中是允许的,并且在继承本身是一个实现细节时使用,例如对于从vector 继承的stack 类,但不想公开向量接口。在这种情况下,B::method 将是一个不可访问的功能,但 A::method 工作正常,即使对象是 B 实例。

正如 Kerrek SB 所说,Java 在这里保护您免受一类错误的影响,但代价是删除了合法选项。

【讨论】:

  • 所以要清楚,在你的例子中,B 类从A 私有继承:你描述的是B 可能发现有必要覆盖一些公共method 的情况A,但也可能不想公开method
  • @Robz 没错。仅当通过其基访问对象(以及从其他方法,如果它们在其实现中调用它)访问对象时,才会调用虚拟方法。
【解决方案2】:

至于虚拟私有方法部分,这允许实现NVI pattern,以便您可以在使用继承的情况下进行不变量检查或设置/拆卸。

这是一个带有锁和后置条件验证的示例:

class base {
public:
    virtual ~base() = default;

    // Calls the derived class' implementation in a thread-safe manner.
    // @return A value greater than 42.
    int function() {
        std::lock_guard<std::mutex> guard(mutex);
        auto result = function_impl();
        assert(result > 42);
        return result;
    }

private:
    std::mutex mutex;
    virtual int function_impl() = 0; 
};

class derived : public base {
private:
    virtual int function_impl() override {
        return 0; // Whoops! A bug!
    }
};

在 Java 中,这可以通过受保护的方法来实现,但这会将实现细节泄露给派生类的派生类,这可能是不希望的。

至于私有化其他公共成员部分,如果有人突然将function_impl 设为base 中的public,它不会破坏派生类。我并不是说这样做是个好主意,但 C++ 通常只是假设您知道自己在做什么,因此它是一种非常灵活的语言。

【讨论】:

  • 所以区别在于C++派生类可以覆盖私有方法而Java不允许呢?
  • @BryanChen 如果我理解正确的话,这就是问题的重点。
  • 我看不出这与实际隐藏派生类型中的公共成员函数有何关系。您最初的 ::base::function_impl 已经是私密的。问题询问为什么最初的 public 成员函数在派生类型中设为私有是合法的。
  • 我注意到在锁下执行“任意”派生类代码是有风险的,除非文档非常清楚地表明锁被持有。除非您喜欢死锁或性能不佳的代码:0
  • 隐藏虚拟方法的要点与隐藏任何其他方法的要点相同——您要拒绝访问继承接口的一部分。这种事情通常在 C++ 中完成,尤其是禁用自动生成的复制构造函数或赋值运算符。允许它用于虚拟方法似乎没有意义,但前提是人们将继承与接口继承混为一谈。 实现继承 是非常合乎逻辑的,您可以在其中继承,但想要公开非继承的 API。 C++ 支持使用 private 继承关键字进行此批发。
猜你喜欢
  • 1970-01-01
  • 2010-12-10
  • 2016-04-02
  • 1970-01-01
  • 2022-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多