调用C++中的私有成员函数

Calling private member function in C++

本文关键字:成员 函数 C++ 调用      更新时间:2023-10-16

我来自Java,在那里不允许减少派生类中的访问修饰符。对于instnace,以下内容不是用Java编译的:

public class A{
    public void foo(){ }
}
public class B extends A{
    @Override
    private void foo(){ }  //compile-error
}

但是,在C++中,这很好:

struct A {
    A(){ }
    virtual ~A(){ }
    A(A&&){ }
public:
    virtual void bar(){ std::cout << "A" << std::endl; }
};
struct B : public A{
private:
    virtual void bar(){ std::cout << "B" << std::endl; }
};
int main()
{
    A *a = new B;
    a -> bar(); //prints B
}

演示

它在哪里有用?此外,这样做安全吗?

正如您所观察到的,访问说明符的工作原理是基于指针的类型,而不是基于动态类型。因此,在这种情况下,在B中指定private只意味着不能通过指向B的指针来访问函数。因此,在客户端不应该使用指向B(或在堆栈上创建B)的指针的情况下,这可能有助于锁定事物。基本上,在B的构造函数是私有的,并且您通过返回unique_ptr<A>的工厂创建B(可能还有A的其他子级)的情况下。在这种情况下,您可以将B的所有方法指定为私有方法。原则上,这可以防止客户端通过向下动态投射unique_ptr,然后直接访问B的接口来"滥用"接口。

不过,我真的不认为应该这样做;它是一种比C++更Java-y的方法。通常,如果客户端希望在堆栈上使用派生对象,而不是通过基类指针在堆上使用,那么他们应该能够。它提供了更好的性能,而且更容易推理。它在泛型代码中也能更好地工作。

编辑:我想我应该澄清一下。考虑以下代码:

enum class Impl {FIRST, SECOND, THIRD};
unique_ptr<A> create(Impl i) {
  ...
}

假设这是创建使用A接口的具体实例的唯一方法。我可能希望派生类是纯粹的实现细节。例如,我可以在不同的类中实现这三个实现中的每一个,然后决定将三个实现的两个集中到一个具有不同选项的类中,等等。这与用户无关;他们的世界只是A的接口加上CCD_ 2函数。但现在假设一个用户碰巧查看了源代码,并知道FIRST实现是使用B实现的。他们想要更好的性能,所以他们这样做:

auto a = create(Impl::FIRST);
auto b = dynamic_cast<B *>(a.get());
// Use b below, potentially avoiding vtable

如果用户有这样的代码,而您删除或重命名了类B,那么他们的代码就会中断。通过使B的所有方法都是私有的,可以使B指针对用户毫无用处,从而确保用户按预期使用接口。

正如我之前所说,我并不特别提倡用C++进行这种编程。但在某些情况下,您确实需要将派生类作为纯粹的实现细节;在这种情况下,更改访问说明符可以帮助强制执行它。