同一成员函数的公有和私有访问

3
我有一个类(A类),专门设计供其他人编写的类继承。我还有另一个类(B类),也继承自A类。
B类需要访问一些不应该被其他继承类访问的A类成员函数。
因此,这些A类成员函数应该对B类公开,但对其他类私有。
如何在不使用'friend'关键字的情况下解决这个问题?
谢谢。
编辑:为何需要这样做的示例。
class A
{
public:
  void PublicFunc()
  {
    PrivateFunc();
    // and other code
  }
private:
  virtual void PrivateFunc();
};

class B : public class A
{
private:
  virtual void PrivateFunc()
  {
    //do something and call A's PrivateFunc
    A::PrivateFunc(); // Can't, it's private!
  }
};

你能否请写一下为什么需要以那种方式? - Johannes Schaub - litb
代码示例展示了为什么你在语法上需要它,而不是为什么你在设计上需要它。对于这样一个微不足道的情况,友元函数确实发挥了应有的作用。会有多少个派生类需要友元访问权限呢?大于1个吗? - gimpf
6个回答

4
你做不到。这就是朋友存在的意义。
另一种选择是改变你的程序设计/架构。但是如果需要更多上下文提示,我可以提供一些建议。

3
你所说的是:A有两组子类。其中一组应该可以访问,而另一组则不应该。只让B这一组子类“看到”A的成员似乎不太对。
如果你的意思是:只有我们可以使用此部分功能,而我们的客户不能,那么还有其他方法。
通过继承实现功能重用常常会遇到这种问题。如果你采用聚合来实现重用,则可能会避免这种问题。
建议:
// separate the 'invisible' from the 'visible'.
class A_private_part {
protected: 
  int inherited_content();
public:
  int public_interface();          
};

class B_internal : public A_private_part {
};

class A_export : private A_private_part {
public:
    int public_interface() { A_private_part::public_interface(); }
};

// client code
class ClientClass : public A_export {
};

但更好的方法是采用聚合方式,将当前的“A”分为可见部分和不可见部分:

class InvisibleFunctionality {
};

class VisibleFunctionality {
};

class B {
    InvisibleFunctionality m_Invisible;
    VisibleFunctionality m_Visible;
};

// client code uses VisibleFunctionality only
class ClientClass {
    VisibleFunctionality m_Visible;
};

看起来私有继承确实可以解决我的问题。谢谢! - Igor
这两个解决方案(以及我最初的尝试)的问题是'B'和其他派生自'A_export'的类型没有共同的基类,因此无法通过相同的API进行传递。为了解决这个问题,我认为您需要使用多重继承和虚基类(如我的最后一个示例)。 - Richard Corden
这完全是真的。Igor,你的问题需要吗?B和客户端的类是否需要通过同一个API传递?如果是的话,你将需要另一个解决方案! - xtofl
是的,那也是真的...如果是这样的话(因为我没有足够的时间重新设计整个包),我将只允许所有A的函数是公共的。 - Igor

1

我认为你在这里有一个更大的问题。你的设计似乎不太合理。

1)我认为“friend”构造本身就存在问题。

2)如果“friend”不是你想要的,你需要重新审视你的设计。

我认为你要么需要做一些只是完成工作的事情,使用“friend”,要么开发一个更强大的架构。看看some design patterns,我相信你会找到一些有用的东西。

编辑:

看了你的示例代码后,你肯定需要重新设计。A类可能不在你的控制范围内,所以有点棘手,但也许你想重新设计B类,将其变成一个“has-a”类,而不是一个“is-a”类。

public Class B
{
    B() 
    {

    }

    void someFunc()
    {
       A a; //the private functions is now called and a will be deleted when it goes out of scope
    }

};

1

好的——如果你想要完全符合你描述的需求,那么“友元”是最佳方案。每个编码标准都建议不要使用“友元”,但如果替代设计更复杂,那么也许值得例外。

要解决没有“友元”的问题,需要采用不同的架构

一种解决方案可能是使用pImpl惯用语的形式,其中“B”源自内部实现对象,而其他客户端则源自外部类。

另一种可能是在“A”和“其他客户端”之间放置一个额外的继承层。类似于:

class A {
public:
  void foo ();
  void bar ();
};

class B : public A {  // OK access to both 'foo' and 'bar'
};

class ARestricted : private A {
public:
  inline void foo () { A::foo (); };    // Forwards 'foo' only
};

然而,这个解决方案仍然存在问题。'ARestricted' 无法转换为 'A',因此需要通过其他“getter”来解决。但是,您可以以一种不会被意外调用的方式命名此函数:
  inline A & get_base_type_A_for_interface_usage_only () { return *this; }

在尝试考虑其他解决方案后并假设您的层次结构需要如您所描述的,我建议您只需使用“friend”关键字! 编辑:因此xtofl建议将类型“A”重命名为“AInternal”,将类型“ARestricted”重命名为“A”。
这个方法可行,但是我注意到'B'不再是'A'。不过,“AInternal”可以被虚拟继承-然后'B'可以从'AInternal'和'A'两者继承!
class AInternal {
public:
  void foo ();
  void bar ();
};

class A : private virtual AInternal {
public:
  inline void foo () { A::foo (); };    // Forwards 'foo' only
};

// OK access to both 'foo' and 'bar' via AInternal
class B : public virtual AInternal, public A {
public:
  void useMembers ()
  {
    AInternal::foo ();
    AInternal::bar ();
  }
};

void func (A const &);

int main ()
{
  A a;
  func (a);

  B b;
  func (b);
}

当然,现在你有虚拟基类和多重继承了!嗯...那么,这是比单个friend声明更好还是更糟呢?


如果你将A重命名为A_Internal,将ARestricted重命名为A,那么你就达到了你想要的效果。 - xtofl

1

我觉得这是一个有趣的挑战。以下是我解决问题的方法:

class AProtectedInterface
{
public:
    int m_pi1;
};

class B;
class A : private AProtectedInterface
{
public:
    void GetAProtectedInterface(B& b_class);

    int m_p1;
};

class B : public A
{
public:
    B();
    void SetAProtectedInterface(::AProtectedInterface& interface);

private:
    ::AProtectedInterface* m_AProtectedInterface;
};

class C : public A
{
public:
    C();
};

C::C()
{
    m_p1 = 0;
//    m_pi1 = 0; // not accessible error
}

B::B()
{
    GetAProtectedInterface(*this);

    // use m_AProtectedInterface to get to restricted areas of A
    m_p1 = 0;
    m_AProtectedInterface->m_pi1 = 0;
}

void A::GetAProtectedInterface(B& b_class)
{
    b_class.SetAProtectedInterface(*this);
}

void B::SetAProtectedInterface(::AProtectedInterface& interface)
{
    m_AProtectedInterface = &interface;
}

如果您经常使用这种模式,可以通过使用模板来减少代码量。
template<class T, class I>
class ProtectedInterfaceAccess : public I
{
public:
    void SetProtectedInterface(T& protected_interface)
    {
        m_ProtectedInterface = &protected_interface;
    }

protected:
    T& GetProtectedInterface()
    {
        return *m_ProtectedInterface;
    }

private:
    T* m_ProtectedInterface;
};

template<class T, class I>
class ProtectedInterface : private T
{
public:
    void SetupProtectedInterface(I& access_class)
    {
        access_class.SetProtectedInterface(*this);
    }
};

class Bt;
class At : public ProtectedInterface <::AProtectedInterface, Bt>
{
public:
    int m_p1;
};

class Bt : public ProtectedInterfaceAccess<::AProtectedInterface, At>
{
public:
    Bt();
};

class Ct : public At
{
public:
    Ct();
};

Ct::Ct()
{
    m_p1 = 0;
    // m_pi1 = 0; // not accessible error
}

Bt::Bt()
{
    SetupProtectedInterface(*this);

    m_p1 = 0;
    GetProtectedInterface().m_pi1 = 0;
}

0
如果我理解正确:
  • A 将由其他开发人员进行子类化。
  • B 将由其他开发人员进行子类化,并继承自 A。
  • A 有一些方法,你不希望通过 B 对外部开发人员可见。
我认为这不能在不使用 friend 的情况下完成。我不知道有什么办法可以使超类的成员仅对直接继承者可用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接