【问题标题】:Calling private member function in C++ [duplicate]在 C++ 中调用私有成员函数 [重复]
【发布时间】:2016-03-28 06:16:55
【问题描述】:

我来自 Java,它不允许在派生类中减少访问修饰符。例如,以下内容在 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
}

DEMO

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

【问题讨论】:

  • 顺便说一句,结构 A 中的 public 修饰符什么都不做,因为结构中的所有内容默认情况下都是公共的。
  • 就个人而言,我不建议更改访问级别,因为我认为这可能会造成混淆。但是,我不知道为什么它会被认为是不安全的,也不知道它为什么有用。相关的是,拥有一个由非虚拟公共函数调用的私有虚拟函数会很有用。请参阅 NVI(非虚拟接口)习语。
  • @JamesAdkison 这是“不安全的”,因为它让外部代码直接调用私有函数,这与私有函数的全部观点相矛盾。
  • @immibis 我的意思是从 UB 的角度来看是不安全的。并且基类打算将其公开,因此 IMO 派生类期望任何比公共访问更少的东西是错误的。

标签: java c++ inheritance virtual-functions


【解决方案1】:

正如您所观察到的,访问说明符根据指针的类型工作,而不是根据动态类型。因此,在这种情况下,在 B 中指定 private 仅意味着无法通过指向 B 的指针访问函数。因此,如果客户端不应该使用指向 B 的指针(或在堆栈上创建 B),这对于保持锁定状态很有用)。基本上,如果 B 的构造函数是私有的,并且您通过返回 unique_ptr&lt;A&gt; 的工厂创建 B(可能还有 A 的其他子代)。在这种情况下,您可以将 B 的所有方法指定为私有方法。原则上,这可以防止客户端通过向下动态转换 unique_ptr 然后直接访问 B 的接口来“滥用”接口。

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

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

enum class Impl {FIRST, SECOND, THIRD};

unique_ptr<A> create(Impl i) {
  ...
}

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

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

如果用户有这样的代码,而您删除或重命名 B 类,他们的代码将会中断。通过将 B 的所有方法设为私有,您可以使 b 指针对用户无用,确保他们按预期使用接口。

正如我之前所说,我并不特别提倡在 C++ 中以这种方式编程。但是在某些情况下,您确实需要派生类是纯粹的实现细节;在这些情况下,更改访问说明符有助于强制执行。

【讨论】:

    猜你喜欢
    • 2013-01-09
    • 1970-01-01
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    • 2020-03-22
    • 2015-12-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多