私有方法的动态绑定:Java与C++;

私有方法的动态绑定:Java与C++;,java,c++,private,dynamic-binding,Java,C++,Private,Dynamic Binding,这在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++中是允许的: clas

这在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++中的这种行为背后的逻辑是什么?

>记住,<>代码>方法>代码>的可见性完全在编译时得到解决,C++没有运行时验证器的概念。编译器看到的是一个虚拟的

a::方法
,它不是私有的。具体实现被声明为私有,但这仅在以编译器可见的方式直接调用此实现时才相关,即,如果您试图通过
B
调用它来直接访问它

逻辑是通过下面的例子来说明的:想象一下,如果代码> B >代码>没有继承<代码> < <代码> >,但私下里——这是C++中允许的,当继承本身是实现细节时,例如,对于<代码>堆栈类继承自<代码>向量< /代码>,但不想暴露向量接口。在这种情况下,

B::method
的一个特性是不可访问的,但是
a::method
工作正常,即使对象是
B
实例


正如Kerrek SB所说,Java正在以删除合法选项为代价保护您不受一类错误的影响。

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

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

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!
    }
};
类基{
公众:
virtual~base()=默认值;
//以线程安全的方式调用派生类的实现。
//@返回一个大于42的值。
int函数(){
std::锁定保护(互斥);
自动结果=函数_impl();
断言(结果>42);
返回结果;
}
私人:
std::互斥互斥;
虚int函数_impl()=0;
};
派生类:公共基{
私人:
虚int函数_impl()重写{
返回0;//哎呀!一个bug!
}
};
在Java中,这可以通过受保护的方法实现,但这会将实现细节泄露给派生类的派生类,而这些派生类可能是不需要的


至于私有化的公共成员部分,如果有人突然在
base
中将
function\u impl
公开,它不会破坏派生类。我并不是说这是一个很好的想法,但是C++通常假设你知道你在做什么,因此它是一种如此灵活的语言。

java优化了90%种用例。C++优化了10%,java更为手持式。java胡过于约束C++,希望大多数“理智”程序适合约束,因此给你更少的出错空间。由于是虚拟的,所以它会像设计的那样具有多态性。@sehe我理解你的意思,但是如果你在
A::method
B::method
中的每一个中都放置一个打印,那么很难证明调用了
A::method
,只有后者的打印才能执行。所以不同的是C++中派生类可以重写私有方法,而java不允许它?@ BryanChen,如果我正确理解它,那就是问题的关键所在。我不知道这与在派生类型中实际隐藏公共成员函数有关。您原来的
::base::function_impl
一开始就已经是私有的。这个问题问为什么一个最初的公共成员函数在派生类型中变为私有是合法的。我要指出的是,在锁下执行“任意”派生类代码是有风险的,除非文档非常清楚地表明锁被持有。除非您喜欢死锁或性能差的代码:0隐藏虚拟方法的点与隐藏任何其他方法的点相同-您希望拒绝访问继承接口的一部分。这类事情经常在C++中完成,特别是禁用自动生成的复制构造函数或赋值操作符。允许它作为虚拟方法似乎毫无意义,但前提是将继承与接口继承合并在一起。对于实现继承来说,这是非常合乎逻辑的,您继承了实现继承,但希望公开继承的API的非继承部分。C++支持用私下< /COD>继承关键字来完成这一点。因此,在示例中,类<代码> b/COD>私下继承从类<代码> A<代码>:您描述的是一个情况:<代码> B<代码>可能需要重写一些公共代码<代码> <代码> > <代码> <代码>,但也可能不想公开
方法
?@Robz。只有当对象通过其基访问时(以及从其他方法访问时,如果它们在实现中调用虚方法),才会调用虚方法。