penelope
penelope

Reputation: 8428

Accessing a protected member of a superclass-typed member object - an elegant solution

First off, I know I can not do it, and I think it's not a duplicate questions (this and this questions deal with the same problem, but they only want an explanation of why it does not work).

So, I have a similar concept of classes and inheritance and I would, somehow, elegantly, want to do something that's forbidden. Here's a very simple code snippet that reflects what I want to do:

#include <iostream>

class A{
protected:
    int var;
    std::vector <double> heavyVar;
public:
    A() {var=1;}
    virtual ~A() {}
    virtual void func() {
        std::cout << "Default behavior" << this->var << std::endl;
    }

    // somewhere along the way, heavyVar is filled with a lot of stuff
};

class B: public A{
protected:
    A* myA;
public:
    B(A &a) : A() {
        this->myA = &a;
        this->var = this->myA->var;
             // copy some simple data, e.g. flags
             // but don't copy a heavy vector variable
    }
    virtual ~B() {}
    virtual void func() {
        this->myA->func();
        std::cout << "This class is a decorator interface only" << std::endl;
    }
};

class C: public B{
private:
    int lotsOfCalc(const std::vector <double> &hv){
        // do some calculations with the vector contents
    }
public:
    C(A &a) : B(a) {
        // the actual decorator
    }
    virtual ~C() {}
    virtual void func() {
        B::func(); // base functionality
        int heavyCalc = lotsOfCalc(this->myA->heavyVar); // illegal
            // here, I actually access a heavy object (not int), and thus
            // would not like to copy it
        std::cout << "Expanded functionality " << heavyCalc << std::endl;
    }
};

int main(void){
    A a;
    B b(a);
    C c(a);
    a.func();
    b.func();
    c.func();
    return 0;
}

The reason for doing this is that I'm actually trying to implement a Decorator Pattern (class B has the myA inner variable that I want to decorate), but I would also like to use some of the protected members of class A while doing the "decorated" calculations (in class B and all of it's subclasses). Hence, this example is not a proper example of a decorator (not even a simple one). In the example, I only focused on demonstrating the problematic functionality (what I want to use but I can't). Not even all the classes/interfaces needed to implement a Decorator pattern are used in this example (I don't have an abstract base class interface, inherited by concrete base class instances as well as an abstract decorator intreface, to be used as a superclass for concrete decorators). I only mention Decorators for the context (the reason I want a A* pointer).

In this particular case, I don't see much sense in making (my equivalent of) int var public (or even, writing a publicly accessible getter) for two reasons:

Right now, I have two different ways of making my code do what I want it to do, but I don't like neither of them, and I'm searching for a C++ concept that was actually intended for doing something of this sort (I can't be the first person to desire this behavior).

What I have so far and why I don't like it:

1. declaring all the (relevant) inherited classes friends to the base class:

class A{
    ....
    friend class B;
    friend class C;
};

I don't like this solution because it would force me to modify my base class every time I write a new subclass class, and this is exactly what I'm trying to avoid. (I want to use only the 'A' interface in the main modules of the system.)

2. casting the A* pointer into a pointer of the inherited class and working with that

void B::func(){
    B *uglyHack = static_cast<B*>(myA);
    std::cout << uglyHack->var + 1 << std::endl;
}

The variable name is pretty suggestive towards my feelings of using this approach, but this is the one I am using right now. Since I designed this classes, I know how to be careful and to use only the stuff that is actually implemented in class A while treating it as a class B. But, if somebody else continues the work on my project, he might not be so familiar with the code. Also, casting a variable pointer in to something that I am very well aware that it is not just feels pure evil to me.

I am trying to keep this projects' code as nice and cleanly designed as possible, so if anybody has any suggestions towards a solution that does not require the modification of a base class every now and then or usage of evil concepts, I would very much appreciate it.

Upvotes: 4

Views: 1081

Answers (3)

penelope
penelope

Reputation: 8428

I think I found a nice way to do what I want in the inheritance structure I have.

Firstly, in the base class (the one that is a base for all the other classes, as well as abstract base class interface in the Decorator Pattern), I add a friend class declaration only for the first subclass (the one that would be acting as abstract decorator interface):

class A{

    ....
    friend class B;
};

Then, I add protected access functions in the subclass for all the interesting variables in the base class:

class B : public A{

    ...
    protected:
        A *myA;
        int getAVar() {return myA->var;}
        std::vector <double> &getAHeavyVar {return myA->heavyVar;}
};

And finally, I can access just the things I need from all the classes that inherit class B (the ones that would be concrete decorators) in a controlled manner (as opposed to static_cast<>) through the access function without the need to make all the subclasses of B friends of class A:

class C : public B{

    ....
    public:
        virtual void func() {
        B::func(); // base functionality
        int heavyCalc = lotsOfCalc(this->getAHeavyVar); // legal now!
            // here, I actually access a heavy object (not int), and thus
            // would not like to copy it
        std::cout << "Expanded functionality " << heavyCalc << std::endl;
        std::cout << "And also the int: " << this->getAVar << std::endl;
            // this time, completely legal
    }
};

I was also trying to give only certain functions in the class B a friend access (declaring them as friend functions) but that did not work since I would need to declare the functions inside of class B before the friend declaration in class A. Since in this case class B inherits class A, that would give me circular dependency (forward declaration of class B is not enough for using only friend functions, but it works fine for a friend class declaration).

Upvotes: 0

I do believe that you might want to reconsider the design, but a solution to the specific question of how can I access the member? could be:

class A{
protected:
    int var;

    static int& varAccessor( A& a ) {
       return a.var;
    }
};

And then in the derived type call the protected accessor passing the member object by reference:

varAccessor( this->myA ) = 5;

Now, if you are thinking on the decorator pattern, I don't think this is the way to go. The source of the confusion is that most people don't realize that a type has two separate interfaces, the public interface towards users and the virtual interface for implementation providers (i.e. derived types) as in many cases functions are both public and virtual (i.e. the language allows binding of the two semantically different interfaces). In the Decorator pattern you use the base interface to provide an implementation. Inheritance is there so that the derived type can provide the operation for the user by means of some actual work (decoration) and then forwarding the work to the actual object. The inheritance relationship is not there for you to access the implementation object in any way through protected elements, and that in itself is dangerous. If you are passed an object of a derived type that has stricter invariants regarding that protected member (i.e. for objects of type X, var must be an odd number), the approach you are taking would let a decorator (of sorts) break the invariants of that X type that should just be decorated.

Upvotes: 2

Mark B
Mark B

Reputation: 96281

I can't find any examples of the decorator pattern being used in this way. It looks like in C++ it's used to decorate and then delegate back to the decoratee's public abstract interface and not accessing non-public members from it.

In fact, I don't see in your example decoration happening. You've just changed the behavior in the child class which indicates to me you just want plain inheritance (consider that if you use your B to decorate another B the effects don't end up chaining like it would in a normal decoration).

Upvotes: 0

Related Questions