42

Suppose I have three C++ classes FooA, FooB and FooC.

FooA has an member function named Hello, I want to call this function in class FooB, but I don't want class FooC be able to call it. The best way I can figure out to realize this is to declare FooB as a friend class of FooA. But as long as I do this, all FooA's private and protected members will be exposed which is quite unacceptable to me.

So, I wanna know if there is any mechanism in C++(03 or 11) better than friend class which can solve this dilemma.

And I assume it will be nice if the following syntax is possible:

class FooA
{
private friend class FooB:
    void Hello();
    void Hello2();
private:
    void Hello3();
    int m_iData;
};

class FooB
{
    void fun()
    {
        FooA objA;
        objA.Hello()  // right
        objA.Hello2() // right
        objA.Hello3() // compile error
        ojbA.m_iData = 0; // compile error
    }
};

class FooC
{
    void fun()
    {
        FooA objA;
        objA.Hello()  // compile error
        objA.Hello2() // compile error
        objA.Hello3() // compile error
        ojbA.m_iData = 0; // compile error
    }
};
leemes
  • 44,967
  • 21
  • 135
  • 183
Archer
  • 592
  • 1
  • 4
  • 11

9 Answers9

33

I think you can use Attorney-Client here.

In your case example should be like this

class FooA
{
private:
    void Hello();
    void Hello2();
    void Hello3();
    int m_iData;

    friend class Client;
};

class Client
{
private:
   static void Hello(FooA& obj)
   {
      obj.Hello();
   }
   static void Hello2(FooA& obj)
   {
      obj.Hello2();
   }
   friend class FooB;
};

class FooB
{
    void fun()
    {
        FooA objA;
        Client::Hello(objA);  // right
        Client::Hello2(objA); // right
        //objA.Hello3() // compile error
        //ojbA.m_iData = 0; // compile error
    }
};

class FooC
{
    void fun()
    {
        /*FooA objA;
        objA.Hello()  // compile error
        objA.Hello2() // compile error
        objA.Hello3() // compile error
        ojbA.m_iData = 0; // compile error*/
    }
};
ForEveR
  • 55,233
  • 2
  • 119
  • 133
30

There's nothing to make a class a friend of one specific function, but you can make FooB a friend of a "key" class with private constructor, and then have FooA::Hello take that class as an ignored parameter. FooC will be unable to provide the parameter and hence can't call Hello:

Is this key-oriented access-protection pattern a known idiom?

Community
  • 1
  • 1
Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • 1
    Thanks! I was looking for it but could not remember the name :D – Matthieu M. Apr 17 '13 at 08:58
  • "There's nothing to make a class a friend of one specific function" sounds the wrong way around, btw. `friend void foo();` works perfectly fine, but I think you mean that there's no way to grant access to just one of your functions directly. – Xeo Apr 17 '13 at 09:01
  • @Xeo I understand the sentence in the answer correctly. If there was no `friend void foo()` the sentence should be "There's nothing to make one specific function a friend of a class". So for me, the answer is all right. – leemes Apr 17 '13 at 09:04
  • I guess Passkey-pattern is the perfect solution except for one thing: it kind of pollutes the interface with an irrelevant parameter. – Archer Apr 17 '13 at 09:29
5

You can partially expose a class's interfaces to a specified client by inherit it from an interface class.

class FooA_for_FooB
{
public:
    virtual void Hello() = 0;
    virtual void Hello2() = 0;
};

class FooA : public FooA_for_FooB
{
private: /* make them private */
    void Hello() override;
    void Hello2() override;
private:
    void Hello3();
    int m_iData;
};

class FooB
{
    void fun()
    {
        FooA objA;
        FooA_for_FooB &r = objA;
        r.Hello()  // right
        r.Hello2() // right
        objA.Hello3() // compile error
        objA.m_iData = 0; // compile error
    }
};

class FooC
{
    void fun()
    {
        FooA objA;
        objA.Hello()  // compile error
        objA.Hello2() // compile error
        objA.Hello3() // compile error
        objA.m_iData = 0; // compile error
    }
};

Here access control is enhanced by the base class FooA_for_FooB. By a reference of type FooA_for_FooB, FooB can access the members defined within FooA_for_FooB. However, FooC cannot access those members since they have been override as private members in FooA. Your purpose can be achieved by not using the type FooA_for_FooB within FooC, or any other places except FooB, which can be kept without paying much attention.

This approach needs no friend, making things simple.

A similar thing can be done by making everything private in a base class, and selectively wrap-and-expose some of the members as public in the derived class. This approach may sometimes require ugly downcast, though. (Because the base class will become the "currency" among the whole program.)

rolevax
  • 1,670
  • 1
  • 14
  • 21
  • Oh, that's enlightening. I can't believe I left your answer in dust for more than 4 years! Thanks, and sorry for the late-coming thanks. But I think this solution needs some modification, since inside FooC.fun, FooA_for_FooB also can be used as a proxy to access FooA.Hello and FooA.Hello2. To avoid this, I think Hello and Hello2 should be declared as protected members of FooA_for_FooB, and class FooB should be declared as a friend of class FooA_for_FooB. What do you say? – Archer Jun 03 '20 at 12:11
1

No, and this is not really a limitation. To my mind, the limitation is that friend — a blunt weapon for hacking around design flaws — exists in the first place.

Your class FooA has no business knowing about FooB and FooC and "which one should be able to use it". It should have a public interface, and not care who can use it. That's the point of the interface! Calling functions within that interface should always leave the FooA in a nice, safe, happy, consistent state.

And if your concern is that you might accidentally use the FooA interface from somewhere you didn't mean to, well, simply don't do that; C++ is not a language suited to protecting against these kinds of user errors. Your test coverage should suffice in this case.

Strictly speaking, I'm sure you can obtain the functionality you're after with some ghastly complicated "design pattern" but, honestly, I wouldn't bother.

If this is a problem for the semantics of your program's design, then I politely suggest that your design has a flaw.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 9
    This would be true if "one class" were the only natural unit of encapsulation in C++. Since it isn't, this answer contains a number of inaccuracies. At the very least, `friend` *functions* are not a blunt weapon, they don't exist to hack around design flaws, and classes do have a business knowing about them. So *at most* your criticisms should be restricted to friend classes, although in fact there are also cases where the natural (non-hacky, non-flawed) unit of encapsulation is a group of friendly classes. – Steve Jessop Apr 17 '13 at 09:02
  • 1
    "And if your concern is that you might accidentally use the FooA interface from somewhere you didn't mean to, well, simply don't do that;" -- having used mostly Python lately, I do have a lot of sympathy for this. A lot of the time and energy spent worrying about access control can be saved by making everything public and then RTFM to not call unpublished functions. But it's a style thing, and people who want to use access control at all find they're going to have to use it well to get any benefit from it. – Steve Jessop Apr 17 '13 at 09:05
  • 1
    @SteveJessop while I agree that neither `friend` functions nor `friend` classes are *always* a blunt weapon, they are used as such in most cases. And the OP's class constellation and requirement for "restricted `friend`" clearly has a smell. – Arne Mertz Apr 17 '13 at 09:24
  • @SteveJessop: `This would be true if "one class" were the only natural unit of encapsulation in C++. Since it isn't` ... I suggest that it is. – Lightness Races in Orbit Apr 17 '13 at 09:33
  • @LightnessRacesinOrbit: I suggest that you're mistaken. For example it's totally unnecessary for a container to publish the interface used by its iterator. I also like friend free function operator overloads, but I suppose you'd argue that they should all call published member functions called things like `add` and `increment` and `printToStream`. I can't claim that's *wrong*, I just think it's unutterably tedious to provide the interface twice ;-) – Steve Jessop Apr 17 '13 at 09:40
  • @SteveJessop: Whilst I enjoyed the strawman, I have not and would not argue any such thing. I do concede that operators imbue something of a "requirement" for `friend` in order to avoid horrible nastinesses, but that this is more of a flaw in C++ than a reason that `friend` is okay _in the OP's scenario_ or a reason that classes are no longer unitary in encapsulation. – Lightness Races in Orbit Apr 17 '13 at 09:49
  • @LightnessRacesinOrbit: Sure, C++ *could* provide a way for classes to define those operators without providing `friend` in general, it just happens not to. And iterators? I'll grant that it doesn't *look* like `FooB` here is a utility class providing part of the desired functionality of `FooA`, but then it's hard to judge the practical uses of classes named `Foo`. – Steve Jessop Apr 17 '13 at 10:04
  • @Steve Honestly I don't see that iterators break the encapsulation of the container over whose data they iterate. They are simply a different view over the same underlying data. Perhaps they break the encapsulation of the data, but I don't see that they break the encapsulation of the actual container's available operations and semantics. – Lightness Races in Orbit Apr 17 '13 at 10:05
  • @LightnessRacesinOrbit: OK, so the reason I raise them as an example, is that we can't tell whether `FooB` here is in any sense analogous to an iterator in needed privileged access to `FooA`. Quite possibly it isn't, since getting into a mess over encapsulation is a common error, but I think it's at least plausible that `FooA` *does* have some business knowing about `FooB`. It can't be stated categorically that it does not. That said, a container wouldn't normally bother using a passkey, it would "trust" the iterator with full friend privileges. – Steve Jessop Apr 17 '13 at 10:07
  • @Steve: Do iterators require privileged access to the container? – Lightness Races in Orbit Apr 17 '13 at 10:09
  • @LightnessRacesinOrbit: well, in principle you could certainly design around it by doing all the "real work" in a class that has public-everything, accessed by both the container and the iterator. Questioner could use that approach here. In practice that class might exist naturally (like a map node), but I wouldn't add it solely to avoid typing `friend`. And even if it exists and its members are *public*, I wouldn't *publish* it, so the unit of encapsulation would still be "two or three classes" (container, iterator, const_iterator). – Steve Jessop Apr 17 '13 at 10:16
  • @Steve: I'm asking whether iterators as implemented in the stdlib require privileged access to standard containers, as I genuinely don't know. My intuition says they shouldn't, but I'm not an iterators expert. _[edit: uh of course they do. duh. Yes okay I guess there's a hole in my theory there then. I still stand by it for the general case, though.]_ – Lightness Races in Orbit Apr 17 '13 at 10:21
  • @LightnessRacesinOrbit: I'm just looking at gcc, `grep friend include/c++/bits/*`, is surprising me both with things that are friends and some things that aren't :-) Most instances of `friend` are functions, the only terrifying cluster-friends I've spotted are in `locale_classes.h`. – Steve Jessop Apr 17 '13 at 10:23
1

The safest solution is to use another class as the "go-between" for your two classes, rather than make one of them a friend. One way to do this is suggested in the answer by @ForEveR, but you can also do some searching about proxy classes and other design patterns that can apply.

Jerome Baldridge
  • 518
  • 2
  • 13
  • I learning about proxy classes form [here](http://stackoverflow.com/questions/994488/what-is-proxy-class-in-c), and it's really useful, thank you! – Archer Apr 20 '13 at 08:33
1

You'll need inheritance. Try this:

// _ClassA.h
class _ClassA
{
  friend class ClassA;
private:
  //all your private methods here, accessible only from ClassA and _ClassA.
}

// ClassA.h
class ClassA: _ClassA
{
  friend class ClassB;
private:
  //all_your_methods
}

This way you have: ClassB is the only one to be able to use ClassA. ClassB cannot access _ClassA methods, that are private.

MCL
  • 3,985
  • 3
  • 27
  • 39
1

You can hide private members in a base class, and then make FooA a child and friend of that base class.

// allows us to hide private members from friends of FooA,
// but still allows FooA itself to access them.
class PrivateFooA
{
private:

    friend class FooA;
    
    // only allow FooA to derive from this class
    PrivateFooA() {};
    
    // hidden from friends of FooA
    void Hello3();
    int m_iData;

};

// this class hides some of its private members from friend classes
class FooA : public PrivateFooA
{
private:

    // give FooB access to private methods
    friend class FooB;
    
    void Hello();
    void Hello2();

};

class FooB
{
    void fun()
    {
        FooA objA;
        objA.Hello();  // right
        objA.Hello2(); // right
        objA.Hello3(); // compile error
        ojbA.m_iData = 0; // compile error
    }
};

class FooC
{
    void fun()
    {
        FooA objA;
        objA.Hello();  // compile error
        objA.Hello2(); // compile error
        objA.Hello3(); // compile error
        ojbA.m_iData = 0; // compile error
    }
};

Anything you want to hide from FooB can be put into PrivateFooA (must be a private member), and everything else can be put directly into FooA. FooA will be able to access everything in PrivateFooA just like its own members.

This is more of an expansion of user3737631's answer, but I think it's worth posting because it includes the classes from the OP, the private constructor in PrivateFooA, and some additional comments that I thought would be helpful.

Mashpoe
  • 504
  • 1
  • 9
  • 27
0

The whole idea of friend is to expose your class to a friend.

There are 2 ways you could be more specific about what you expose:

  1. Inherit from FooA, that way only protected and public methods are exposed.

  2. Only befriend a certain method, that way only that method will have access:

.

 friend void FooB::fun();
Yochai Timmer
  • 48,127
  • 24
  • 147
  • 185
-1

I had to do this recently and I didn't like the way these solutions leave a class type dangling around in the current namespace with essentially no purpose. If you REALLY do just want this functionality available to a single class then I would use a different pattern than those mentioned.

class Safety {
protected:
   std::string _Text="";
public:
   Safety(const std::string& initial_text) {
      _Text=initial_text;
   }
   void Print(const std::string& test) {
      std::cout<<test<<" Value: "<<_Text<<std::endl;
   }
};

class SafetyManager {
protected:
   // Use a nested class to provide any additional functionality to 
   // Safety that you want with protected level access. By declaring
   // it here this code only belongs to this class. Also, this method
   // doesn't require Safety to inherit from anything so you're only
   // adding weight for the functionality you need when you need it.
   // You need to be careful about how this class handles this object
   // since it is really a Safety cast to a _Safety. You can't really 
   // add member data to this class but static data is ok.
   class _Safety : Safety {
   public:
      void SetSafetyText(const std::string& new_text) {
         _Text=std::string(new_text);
      }
   };
   
public:
   static void SetSafetyText(Safety* obj, const std::string& new_text) {
      if(obj==nullptr) throw "Bad pointer.";
      _Safety& iobj=*(_Safety*)obj;
      iobj.SetSafetyText(new_text);
   }
};

Then in main (or anywhere else) you can't modify _Text through Safety but you can through SafetyManager (or it's descendants).

#include "Safety.h"

int main() {
   Safety t("Hello World!");
   t.Print("Initial");
   SafetyManager::SetSafetyText(&t, "Brave New World!");
   t.Print("Modified");
/*
   t._Text;                         // not accessible
   Safety::SetSafetyText(&t, "ERR");// doesn't exist
   t.SetSafetyText(&t, "ERR");      // doesn't exist
   _Safety _safety;                 // not accessible
   SafetyManager::_Safety _safety;  // not accessible
*/
}

Some would say that this follows better OOP practices than a friend class because it encapsulates the messy parts a little better and doesn't pass anything down the Safety chain of inheritance. You also don't need to modify the Safety class at all for this technique making it much more modular. These are probably the reasons why many newer languages allow for nested classes but almost nothing else has borrowed the friend concept even though this just adds functionality that is available only to a single class (and doesn't work if Safety is marked final or marked vital parts of it's code as private).

krowe
  • 2,129
  • 17
  • 19