7

Suppose we have two classes:

class Base
{
private:
    int x;
public:
    void f();
};

class Foo
{
    // some variables and methods
};

Now everyone can call Base::f(), but I want only Foo to be able to do so.

In order to achieve this effect, we can make Base::f() private and declare Foo as a friend:

class Base
{
private:
    int x;
    void f();
    friend Foo;
};

The problem with this approach is that Foo has the access to both Base::f() and Base::x (and even to any other private members of Base). But I want Foo to have access only to Base::f().

Is there a way for a class (or a function) to grant an access only to certain private members of another class? Or maybe anyone could suggest a better approach to my problem?

EDIT:

I'll try to specify the access restriction I need. Firstly, Base is an interface in a library (it's an abstract class, in fact). The user uses only the classes derived from Base. Base::f() is called only by Foo which is another class in the library. Hiding Base::f() from the user is important, because only Foo knows when to call it. At the same time, Foo shouldn't mess up the other members of Base.

miloszmaki
  • 1,635
  • 15
  • 21
  • You could make `f()` protected and use protected inheritance I think. – chris Jun 15 '12 at 15:20
  • But `Foo` is not derived from `Base`. – miloszmaki Jun 15 '12 at 15:21
  • It was just a thought. You'd have to derive it. After seeing the `BaseData` answer, I'd go with that, though. – chris Jun 15 '12 at 15:23
  • 1
    The access control mechanism in C++ is not designed for actually security it is designed to stop you from making accidental mistakes. One assumes that since you are writing both classes and as such can add friend then you will know only to call f() from Foo. – Martin York Jun 15 '12 at 16:09

4 Answers4

7

Very hacky, but this will allow very fine grained access.

class Base
{
private:
    int x;
    void f();
    friend class Base_f_Accessor;
};

class Base_f_Accessor
{
private:
    static void f(Base & b) { b.f(); }
    friend class Foo;
}

class Foo
{
    // some variables and methods
};
Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
  • I like this approach, since it allows to hide the implementation details of `Base_f_Accessor` in a .cpp file. This way, `Foo` can also be defined in the .cpp file and not exposed to the user. – miloszmaki Jun 15 '12 at 16:10
4

You can create another class that contains the data for Base like this:

class BaseData {
protected:
    int x;
};

class Base : public BaseData {
    friend class Foo;
    void f ();
};

Now, Foo can access f as a method of Base like you wanted, but not x. Friendship is not commutative. By using protected, x appears private to everyone except those that derived directly from BaseData.

A better approach might be to use multiple inheritance to define Base, and provide Foo access only to those classes you want from which Base derives.

class With_f {
    friend class Foo;
protected:
    virtual void f () = 0;
};

class With_g {
protected:
    virtual void g () = 0;
};

class Base : public With_f, public With_g {
    int x;
    void f () {}
    void g () {}
};

Here, Foo would have to have a With_f pointer to Base, but it could then access the f method. Foo could not access g.

jxh
  • 69,070
  • 8
  • 110
  • 193
  • What would happen if you made `Base` a friend of `BaseData`? Would friends of `Base` be able to access it too? I think that would work better than inheritance if it works like that. – chris Jun 15 '12 at 15:24
  • @chris: A friend of a friend is not a friend in C++. – jxh Jun 15 '12 at 15:26
  • Then that would probably do better than introducing inheritance. – chris Jun 15 '12 at 15:27
  • @chris: Depends on what the programmer wants, I make no judgements on stylistic issues on SO. – jxh Jun 15 '12 at 15:28
  • This approach is also very useful. Unfortunately I can't accept more than one answer ;) – miloszmaki Jun 15 '12 at 16:14
3

There's no easy, non-hackish way to achieve that. C++ simply doesn't have such access control granularity. You can play with some inheritance, but increased complexity outweighs any advantages this access restriction might have. Also, this approach doesn't scale - you can grant increased permissions only to one friend class.

0

Maybe a bit cumbersome, but you could make nested classes where the nesting class is friend, then you can add friends per nested class. This gives some level of granularity:

#include <iostream>

class Nesting
{
  friend class Foo;
  class Nested1
  {
    friend class Nesting;
  public:
    Nested1() : i(3) { }
  private:
    int i;
  } n1;
  class Nested2
  {
    friend class Nesting;
    friend class Foo;
  public:
    Nested2() : j(5) { }
  private:
    int j;
  } n2;
  int f() { return n1.i; }
};

class Foo
{
public:
  Foo(Nesting& n1) : n(n1) { }
  int getJ() { return n.n2.j + n.f(); }
private:
  Nesting& n;
};

int main()
{
  Nesting n;
  Foo foo(n);
  std::cout << foo.getJ() << "\n";
}
stefaanv
  • 14,072
  • 2
  • 31
  • 53