0

I have a class derived from an interface and a friend class of the derived class. I want to access the members of derived class which is instantiated as interface. It looks like this:

Interface:

class AInterface
{
public:
   virtual ~AInterface() = default;
   virtual void set(int a) = 0;
};

Derived class A with friend class B:

class B;
class A : public AInterface
{
public:
   ~A() override {}
   void set(int a) override
   {
      mem = a;
   }
private:
   friend class B;

   int mem = 0;
};

class B:

class B
{
public:
   B() 
   {
     a = new A();
     a->set(3);
   }

   int get_a()
   {
      // Access mem since it's a friend class
      return a->mem;
   }

private:
    AInterface *a;
}

main:

int main()
{
    B *b = new B();
    std::cout << b->get_a() << std::endl;
    return 0;
}

The program does not compile saying AInterface has no member named 'mem'. Do I need getter functions in the interface and implement it in A to achieve this or is there any other way to do it?

kwadhwa
  • 129
  • 1
  • 8

2 Answers2

1

Now work

#include <iostream>
using namespace std;


class AInterface
{
public:
    virtual ~AInterface() = default;
    int getMem() { return mem; }
    virtual void set(int a) = 0;
protected:
    int mem = 0;
};

class A : public AInterface
{
public:
    ~A() override {}
    void set(int a) override
    {
        mem = a;
    }


};

class B
{
public:
    B()
    {
         a = new A{};
        a->set(3);
    }

    int get_a()
    {
        // Access mem since it's a friend class
        return a->getMem();
    }

private:
    AInterface *a;
};
int main()
{
    B *b = new B();
    std::cout << b->get_a() << std::endl;
    return 0;
}
  1. Method which is override by child and is pure, should be virtual.
  2. If each class (child) Interface, variable int mem should be protected in interface. Now works fine, like you want.
  3. Add getter getMem()

Version with friend

#include <iostream>
using namespace std;


class AInterface
{
public:
    virtual ~AInterface() = default;
    virtual void set(int a) = 0;
protected:
    friend class B;
    int mem = 0;
};

class A : public AInterface
{
public:
    ~A() override {}
    void set(int a) override
    {
        mem = a;
    }


};

class B
{
public:
    B()
    {
         a = new A{};
        a->set(3);
    }

    int get_a()
    {
        // Access mem since it's a friend class
        return a->mem;
    }

private:
    AInterface *a;
};
int main()
{
    B *b = new B();
    std::cout << b->get_a() << std::endl;
    return 0;
}
21koizyd
  • 1,843
  • 12
  • 25
  • Why is using `friend` not correct here? By using `friend` the OP retains encapsulation while only exposing it's internals to a very select few. By adding an _accessor_ member function now exposes it to the entire world. – Captain Obvlious Jul 05 '17 at 23:05
  • @CaptainObvlious okey, you've right, always each developer say "You never use `friend", but I never think about that like special access point – 21koizyd Jul 05 '17 at 23:22
0

In your class B.

class B
{
  //...
   int get_a()
   {
      return a->mem;   // but a is a AInterface*  !!!
   }

private:
    AInterface *a;     // That's not an A*, but an AInterface*
};

You have 2 options.

  1. use dynamic_cast<>

     int B::get_a()
     {
         A* p = dynamic_cast<A*>(a);
         if (p)
           return p->mem;  
         // a is not an A*, it's another AInterface*-type object !!!  
                                       // What should you do?
         throw something_or_other();   // throw?
         return -1;                    // return an error code?
       }
       // or maybe add..  so you can check for errors before calling get_a()
       A* B::get_A_ptr() const 
       { 
          return dynamic_cast<A*>(a); 
       }
    

dynamic_cast works fine, but can slow down your app if you need to make frequent reads of a->mem.

  1. Store a in an A*, which is probably what you meant to do from the start...

    class B
    {
      // ...
    private:
      A* a;    // now a->A::mem is visible.
    };
    

Since you explicitly call new A in B's constructor, I think option 2 is better for your case.

Michaël Roy
  • 6,338
  • 1
  • 15
  • 19