views:

271

answers:

6

I have a class (class A) that is designed to be inherited by other classes written by other people. I also have another class (class B), that also inherits from A.

B has to access some A's member functions that shouldn't be accessed by other inheriting classes.

So, these A's member functions should be public for B, but private for others.

How can I solve it without using 'friend' directive?

Thank you.

EDIT: Example why I need it.

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!
  }
};
A: 

If I understand:

  • A will be subclassed by other developers.
  • B will be subclassed by other developers and inherits from A.
  • A has some methods you don't want accessible to outside developers through B.

I don't think this can be done without using friend. There is no way I know of to make members of a superclass available only to direct inheritors.

Dave Swersky
+4  A: 

You can't. That's what friend is for.

An alternative would be to change the design/architecture of your program. But for hints on this I'd need some more context.

gimpf
+1  A: 

I think you have a bigger problem here. Your design doesn't seem sound.

1) I think the 'friend' construct is problematic to begin with

2) if 'friend' isn't what you want, you need to re-examine your design.

I think you either need to do something that just gets the job done, using 'friend' or develop a more robust architecture. Take a look at some design patterns, I'm sure you'll find something useful.

EDIT:

After seeing your sample code, you definitely need to re-arch. Class A may not be under your control, so that's a little tricky, but maybe want you want to re-do Class B to be a "has-a" class instead of an "is-a" class.

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
    }

};
cbrulak
+1  A: 

Well - if you want exactly what you've described, then friend is the best solution. Every coding standard recommends not using friend but where the alternative design is more complex - then maybe it's worth making an exception.

To solve the problem without friend will require a different architecture

One solution might be to use a form of the pImpl idiom where 'B' derives from the inner implementation object, while the other clients derive from the outer class.

Another might be to place an extra layer of inheritance between 'A' and the "other clients". Something like:

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
};

However, this solution still has it's problems. 'ARestricted' cannot convert to an 'A' so this would need to be solved by some other "getter" for 'A'. However, you could name this function in such a way as it cannot be called accidentally:

  inline A & get_base_type_A_for_interface_usage_only () { return *this; }

After trying to think of other solutions, and assuming that your hierarchy needs to be as you describe, I recommend you just use friend!

EDIT: So xtofl suggested renaming the types 'A' to 'AInternal' and 'ARestricted' to 'A'.

That works, except I noticed that 'B' would no longer be an 'A'. However, AInternal could be inherited virtually - and then 'B' could derive from both 'AInternal' and '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);
}

Of course now you have virtual bases and multiple inheritance! Hmmm....now, is that better or worse than a single friend declaration?

Richard Corden
If you then rename A to A_Internal, and ARestricted to A, you're where you want.
xtofl
Problem is that 'B' is not and 'A'. Although....edit to come...
Richard Corden
+3  A: 

What you say is: there are two sets of subclasses of A. One set should have access, the other set shouldn't. It feels wrong to have only one brand of subclasses (i.e. B) 'see' A's members.

If what you mean is: only we can use this part of functionality, while our clients can't, there are other resorts.

(Functionality reuse by inheritance often corners you with this kind of problems. If you go towards reuse by aggregation, you may get around it.)

A suggestion:

// 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 {
};

But better would be to go the aggregation way, and split the current "A" into a visible and an invisible part:

class InvisibleFunctionality {
};

class VisibleFunctionality {
};

class B {
    InvisibleFunctionality m_Invisible;
    VisibleFunctionality m_Visible;
};

// client code uses VisibleFunctionality only
class ClientClass {
    VisibleFunctionality m_Visible;
};
xtofl
Looks like private inheritance can really solve that for me. Thanks!
Igor Oks
The problem with both of these solutions (and with my first attempts) is that 'B' and the other types deriving from 'A_export' do not have a common base - and so cannot be passed through the same API. To fix this I think you need multiple inheritance and virtual bases (as per my last example).
Richard Corden
That is totally true. Igor, is this needed for your problem? Do B and the client's classes need to be passed through the same API? If so, you'll need another solution!
xtofl
Yes, that is also true... If so, looks like (since I don't have enough time to redesign the whole package), I will just let all A's functions be public..
Igor Oks
+1  A: 

I find this a interesting challenge. Here is how I would solve the problem:

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;
}

If you where going to use this sort of pattern all the time, you could reduce the code by using templates.

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;
}
Shane Powell