0

Is it reasonable to do something like this?

Note: this is a Minimal Working Example

class A {
    public:
        int getX() { return x; }
    protected:
        int x;
        virtual void setX(int newX) = 0;
};

// Children can modify X
class Can_Modify_X : public A {
     protected:
         void setX(int newX) { x = newX; }
     private:
         using A::x;
};

// Children can't modify X
class Can_Not_Modify_X : public A {
     private:
         void setX(int newX) { }
         using A::x;
};

I'm aware that I can't simply hide a function because that would violate the Liskov Principle, but doing a private inheritance and specify again all the public methods seems really redundant.

The two classes must have a common parent (even if it is directly one of them), and must not be able to modify x directly.

BONUS: can somebody point me to somewhere defining the exact behaviour of using in this cases? I tried googling it, but with very little success.

Community
  • 1
  • 1
Svalorzen
  • 5,353
  • 3
  • 30
  • 54
  • But that is a pure virtual, it doesn't even matter what visibility it has since its childs can change it when implementing it. Here it only matters so that other entities can't create an A* and call the virtual function from there. Or not? – Svalorzen Dec 04 '12 at 22:51
  • What do you mean "you can't"? It does compile, what am I breaking here? – Svalorzen Dec 04 '12 at 23:00

3 Answers3

1

I'm aware that I can't simply hide a function because that would violate the Liskov Principle.

The exact same concept applies to data members.

Suppose you have a pointer (or reference) to an instance of Can_Modify_X or Can_Not_Modify_X. You can't access or modify data member x through this reference. If you upcast this derived class pointer to a pointer to class A you suddenly can modify data member x. By making x private, you are violating the Liskov substitution principle. It doesn't matter whether x is a data member, a member function, or a type definition. You're violating Liskov substitution, pure and simple.

Derived classes should not hide capabilities provided by a parent class.

David Hammen
  • 32,454
  • 9
  • 60
  • 108
  • But since `x` is protected in my base class, nobody would actually know, would it? The class itself is not instantiable, so no problems there. A pointer to class `A` is not able to access `x` since it is protected. Any addictional class derived from `A` will be able to access `x` as required, since it is protected, so that's OK. The only difference will be for childs of `Can_Modify_X` and `Can_Not_Modify_X`, but that's irrelevant since it is the point of the classes to do that. I can't see any particular case in which this thing might break. Is there even one? – Svalorzen Dec 05 '12 at 01:22
  • I even tried extending `Can_Not_Modify_X` and making a function that cast the child itself to `A` and tried to access `x` like that, but the compiler refused to accept it. – Svalorzen Dec 05 '12 at 02:09
0

What would you expect the following code to do ?

A * a = new Can_Not_Modify_X();
a->setX(10);
Coincoin
  • 27,880
  • 7
  • 55
  • 76
  • This isn't really an answer. – Pete Fordham Dec 04 '12 at 22:58
  • @Pete Now I realize I could have been a little bit more diplomat about it but I believe it is an answer in the sense that it explains why it's not a good idea without breast feeding an answer. 90% (made up stat) of `"Why can't [insert language] do that?"` questions are easily answered by simply asking yourself what you would expect it to do exactly and realize it doesn't make any sense or is so overly complicated that it becomes an implementation nightmare. – Coincoin Dec 05 '12 at 15:32
  • I see that but in any case your example will always fail because setX is either private or protected. It's never public is an of the classes, but that't not relevant to the original question. – Pete Fordham Dec 05 '12 at 16:48
0

This is a subjective question so my answer will be correspondingly subjective.

I'm going to say no it's not reasonable. protected attributes make it really easy for child classes to accidentally mutate state and violate invariants so I suggest avoiding them completely. Then your parent class will maintain x through its public or protected interface (hopefully through a meaningful set of methods and not simply mutators).

Then you don't need to change accessibility in the children as your interface controls access appropriately already.

Not only that, changing the accessibility of members or methods violates the principle of least surprise and will most likely cause problems for your future maintainers.

Mark B
  • 95,107
  • 10
  • 109
  • 188